/*************************************************************************
**
**    OLE 2 Sample Code
**
**    outldoc.c
**
**    This file contains OutlineDoc functions.
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/

#include "outline.h"

#if !defined( OLE_VERSION )
#include <commdlg.h>
#endif


OLEDBGDATA

extern LPOUTLINEAPP g_lpApp;

#ifdef WIN32S
#define GALLOCFLG (GMEM_SHARE | GMEM_ZEROINIT | GMEM_MOVEABLE)
#else
#define GALLOCFLG (GMEM_SHARE | GMEM_ZEROINIT)
#endif

// REVIEW: should use string resource for messages
OLECHAR ErrMsgDocWnd[] = OLESTR("Can't create Document Window!");
OLECHAR ErrMsgFormatNotSupported[] = OLESTR("Clipboard format not supported!");
OLECHAR MsgSaveFile[] = OLESTR("Save existing file ?");
OLECHAR ErrMsgSaving[] = OLESTR("Error in saving file!");
OLECHAR ErrMsgOpening[] = OLESTR("Error in opening file!");
OLECHAR ErrMsgFormat[] = OLESTR("Improper file format!");
OLECHAR ErrOutOfMemory[] = OLESTR("Error: out of memory!");
static OLECHAR ErrMsgPrint[] = OLESTR("Printing Error!");

static BOOL fCancelPrint;    // TRUE if the user has canceled the print job
static HWND hWndPDlg;       // Handle to the cancel print dialog


/* OutlineDoc_Init
 * ---------------
 *
 *  Initialize the fields of a new OutlineDoc object. The object is initially
 *  not associated with a file or an (Untitled) document. This function sets
 *  the docInitType to DOCTYPE_UNKNOWN. After calling this function the
 *  caller should call:
 *      1. OutlineDoc_InitNewFile to set the OutlineDoc to (Untitled)
 *      2. OutlineDoc_LoadFromFile to associate the OutlineDoc with a file.
 *  This function creates a new window for the document.
 *
 *  NOTE: the window is initially created with a NIL size. it must be
 *        sized and positioned by the caller. also the document is initially
 *        created invisible. the caller must call OutlineDoc_ShowWindow
 *        after sizing it to make the document window visible.
 */
BOOL OutlineDoc_Init(LPOUTLINEDOC lpOutlineDoc, BOOL fDataTransferDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

#if defined( INPLACE_CNTR )
   lpOutlineDoc->m_hWndDoc = CreateWindow(
               DOCWNDCLASS,            // 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_CLIPSIBLINGS |
               WS_CHILDWINDOW,
               0, 0,
               0, 0,
               lpOutlineApp->m_hWndApp,// Parent window's handle
               (HMENU)1,               // child window id
               lpOutlineApp->m_hInst,  // Instance of window
               NULL);                  // Create struct for WM_CREATE

#else

   lpOutlineDoc->m_hWndDoc = CreateWindow(
               DOCWNDCLASS,            // Window class name
               NULL,                   // Window's title
               WS_CHILDWINDOW,
               0, 0,
               0, 0,
               lpOutlineApp->m_hWndApp,// Parent window's handle
               (HMENU)1,               // child window id
               lpOutlineApp->m_hInst,  // Instance of window
               NULL);                  // Create struct for WM_CREATE
#endif

   if(! lpOutlineDoc->m_hWndDoc) {
      OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgDocWnd);
      return FALSE;
   }

   SetWindowLong(lpOutlineDoc->m_hWndDoc, 0, (LONG) lpOutlineDoc);

   if (! LineList_Init(&lpOutlineDoc->m_LineList, lpOutlineDoc))
      return FALSE;

   lpOutlineDoc->m_lpNameTable = OutlineDoc_CreateNameTable(lpOutlineDoc);
   if (! lpOutlineDoc->m_lpNameTable )
      return FALSE;

   lpOutlineDoc->m_docInitType = DOCTYPE_UNKNOWN;
   lpOutlineDoc->m_cfSaveFormat = lpOutlineApp->m_cfOutline;
   lpOutlineDoc->m_szFileName[0] = '\0';
   lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName;
   lpOutlineDoc->m_fDataTransferDoc = fDataTransferDoc;
   lpOutlineDoc->m_uCurrentZoom = IDM_V_ZOOM_100;
   lpOutlineDoc->m_scale.dwSxN  = (DWORD) 1;
   lpOutlineDoc->m_scale.dwSxD  = (DWORD) 1;
   lpOutlineDoc->m_scale.dwSyN  = (DWORD) 1;
   lpOutlineDoc->m_scale.dwSyD  = (DWORD) 1;
   lpOutlineDoc->m_uCurrentMargin = IDM_V_SETMARGIN_0;
   lpOutlineDoc->m_nLeftMargin  = 0;
   lpOutlineDoc->m_nRightMargin = 0;
   lpOutlineDoc->m_nDisableDraw = 0;
   OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE);

#if defined( USE_HEADING )
   if (! fDataTransferDoc) {
      if (!Heading_Create((LPHEADING)&lpOutlineDoc->m_heading,
            lpOutlineDoc->m_hWndDoc, lpOutlineApp->m_hInst)) {
         return FALSE;

      }
   }
#endif  // USE_HEADING

#if defined( USE_FRAMETOOLS )
   if (! fDataTransferDoc) {
      lpOutlineDoc->m_lpFrameTools = OutlineApp_GetFrameTools(lpOutlineApp);
      FrameTools_AssociateDoc(
            lpOutlineDoc->m_lpFrameTools,
            lpOutlineDoc
      );
   }
#endif  // USE_FRAMETOOLS

#if defined( OLE_VERSION )
   /* OLE2NOTE: perform initialization required for OLE */
   if (! OleDoc_Init((LPOLEDOC)lpOutlineDoc, fDataTransferDoc))
      return FALSE;
#endif  // OLE_VERSION

   return TRUE;
}


/* OutlineDoc_InitNewFile
 * ----------------------
 *
 *  Initialize the OutlineDoc object to be a new (Untitled) document.
 *  This function sets the docInitType to DOCTYPE_NEW.
 */
BOOL OutlineDoc_InitNewFile(LPOUTLINEDOC lpOutlineDoc)
{
#if defined( OLE_VERSION )
   // OLE2NOTE: call OLE version of this function instead
   return OleDoc_InitNewFile((LPOLEDOC)lpOutlineDoc);

#else

   OleDbgAssert(lpOutlineDoc->m_docInitType == DOCTYPE_UNKNOWN);

   // set file name to untitled
   // REVIEW: should load from string resource
   lstrcpyW(lpOutlineDoc->m_szFileName, UNTITLED);
   lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName;
   lpOutlineDoc->m_docInitType = DOCTYPE_NEW;

   if (! lpOutlineDoc->m_fDataTransferDoc)
      OutlineDoc_SetTitle(lpOutlineDoc, FALSE /*fMakeUpperCase*/);

   return TRUE;

#endif      // BASE OUTLINE VERSION
}


/* OutlineDoc_CreateNameTable
 * --------------------------
 *
 * Allocate a new NameTable of the appropriate type. Each document has
 * a NameTable and a LineList.
 *  OutlineDoc --> creates standard OutlineNameTable type name tables.
 *  ServerDoc  --> creates enhanced SeverNameTable type name tables.
 *
 *      Returns lpNameTable for successful, NULL if error.
 */
LPOUTLINENAMETABLE OutlineDoc_CreateNameTable(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINENAMETABLE lpOutlineNameTable;

   lpOutlineNameTable = (LPOUTLINENAMETABLE)New(
         (DWORD)sizeof(OUTLINENAMETABLE)
   );

   OleDbgAssertSz(lpOutlineNameTable != NULL,"Error allocating NameTable");
   if (lpOutlineNameTable == NULL)
      return NULL;

   // initialize new NameTable
   if (! OutlineNameTable_Init(lpOutlineNameTable, lpOutlineDoc) )
      goto error;

   return lpOutlineNameTable;

error:
   if (lpOutlineNameTable)
      Delete(lpOutlineNameTable);
   return NULL;
}


/* OutlineDoc_ClearCommand
 * -----------------------
 *
 *      Delete selection in list box by calling OutlineDoc_Delete
 */
void OutlineDoc_ClearCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   int i;
   int nNumSel;
   LINERANGE lrSel;

   nNumSel=LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);

   OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );
   for(i = 0; i < nNumSel; i++)
      OutlineDoc_DeleteLine(lpOutlineDoc, lrSel.m_nStartLine);

   OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

   LineList_RecalcMaxLineWidthInHimetric(lpLL, 0);
}


/* OutlineDoc_CutCommand
 * ---------------------
 *
 * Cut selection to clipboard
 */
void OutlineDoc_CutCommand(LPOUTLINEDOC lpOutlineDoc)
{
   OutlineDoc_CopyCommand(lpOutlineDoc);
   OutlineDoc_ClearCommand(lpOutlineDoc);
}


/* OutlineDoc_CopyCommand
 * ----------------------
 *  Copy selection to clipboard.
 *  Post to the clipboard the formats that the app can render.
 *  the actual data is not rendered at this time. using the
 *  delayed rendering technique, Windows will send the clipboard
 *  owner window either a WM_RENDERALLFORMATS or a WM_RENDERFORMAT
 *  message when the actual data is requested.
 *
 *    OLE2NOTE: the normal delayed rendering technique where Windows
 *    sends the clipboard owner window either a WM_RENDERALLFORMATS or
 *    a WM_RENDERFORMAT message when the actual data is requested is
 *    NOT exposed to the app calling OleSetClipboard. OLE internally
 *    creates its own window as the clipboard owner and thus our app
 *    will NOT get these WM_RENDER messages.
 */
void OutlineDoc_CopyCommand(LPOUTLINEDOC lpSrcOutlineDoc)
{
#if defined( OLE_VERSION )
   // Call OLE version of this function instead
   OleDoc_CopyCommand((LPOLEDOC)lpSrcOutlineDoc);

#else
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPOUTLINEDOC lpClipboardDoc;

   OpenClipboard(lpSrcOutlineDoc->m_hWndDoc);
   EmptyClipboard();

   /* squirrel away a copy of the current selection to the ClipboardDoc */
   lpClipboardDoc = OutlineDoc_CreateDataTransferDoc(lpSrcOutlineDoc);

   if (! lpClipboardDoc)
      return;     // Error: could not create DataTransferDoc

   lpOutlineApp->m_lpClipboardDoc = (LPOUTLINEDOC)lpClipboardDoc;

   SetClipboardData(lpOutlineApp->m_cfOutline, NULL);
   SetClipboardData(CF_TEXT, NULL);

   CloseClipboard();

#endif  // ! OLE_VERSION
}


/* OutlineDoc_ClearAllLines
 * ------------------------
 *
 *      Delete all lines in the document.
 */
void OutlineDoc_ClearAllLines(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   int i;

   for(i = 0; i < lpLL->m_nNumLines; i++)
      OutlineDoc_DeleteLine(lpOutlineDoc, 0);

   LineList_RecalcMaxLineWidthInHimetric(lpLL, 0);
}


/* OutlineDoc_CreateDataTransferDoc
 * --------------------------------
 *
 *      Create a document to be use to transfer data (either via a
 *  drag/drop operation of the clipboard). Copy the selection of the
 *  source doc to the data transfer document. A data transfer document is
 *  the same as a document that is created by the user except that it is
 *  NOT made visible to the user. it is specially used to hold a copy of
 *  data that the user should not be able to change.
 *
 *  OLE2NOTE: in the OLE version the data transfer document is used
 *      specifically to provide an IDataObject* that renders the data copied.
 */
LPOUTLINEDOC OutlineDoc_CreateDataTransferDoc(LPOUTLINEDOC lpSrcOutlineDoc)
{
#if defined( OLE_VERSION )
   // Call OLE version of this function instead
   return OleDoc_CreateDataTransferDoc((LPOLEDOC)lpSrcOutlineDoc);

#else
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPOUTLINEDOC lpDestOutlineDoc;
   LPLINELIST lpSrcLL = &lpSrcOutlineDoc->m_LineList;
   LINERANGE lrSel;
   int nCopied;

   lpDestOutlineDoc = OutlineApp_CreateDoc(lpOutlineApp, TRUE);
   if (! lpDestOutlineDoc) return NULL;

   // set the ClipboardDoc to an (Untitled) doc.
   if (! OutlineDoc_InitNewFile(lpDestOutlineDoc))
      goto error;

   LineList_GetSel(lpSrcLL, (LPLINERANGE)&lrSel);
   nCopied = LineList_CopySelToDoc(
         lpSrcLL,
         (LPLINERANGE)&lrSel,
         lpDestOutlineDoc
   );

   return lpDestOutlineDoc;

error:
   if (lpDestOutlineDoc)
      OutlineDoc_Destroy(lpDestOutlineDoc);

   return NULL;

#endif  // ! OLE_VERSION
}


/* OutlineDoc_PasteCommand
 * -----------------------
 *
 * Paste lines from clipboard
 */
void OutlineDoc_PasteCommand(LPOUTLINEDOC lpOutlineDoc)
{
#if defined( OLE_VERSION )
   // Call OLE version of this function instead
   OleDoc_PasteCommand((LPOLEDOC)lpOutlineDoc);

#else

   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPLINELIST lpLL = (LPLINELIST)&lpOutlineDoc->m_LineList;
   int nIndex;
   int nCount;
   HGLOBAL hData;
   LINERANGE lrSel;
   UINT uFormat;

   if (LineList_GetCount(lpLL) == 0)
      nIndex = -1;    // pasting to empty list
   else
      nIndex=LineList_GetFocusLineIndex(lpLL);

   OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );

   OpenClipboard(lpOutlineDoc->m_hWndDoc);

   uFormat = 0;
   while(uFormat = EnumClipboardFormats(uFormat)) {
      if(uFormat == lpOutlineApp->m_cfOutline) {
         hData = GetClipboardData(lpOutlineApp->m_cfOutline);
         nCount = OutlineDoc_PasteOutlineData(lpOutlineDoc, hData, nIndex);
         break;
      }
      if(uFormat == CF_TEXT) {
         hData = GetClipboardData(CF_TEXT);
         nCount = OutlineDoc_PasteTextData(lpOutlineDoc, hData, nIndex);
         break;
      }
   }

   lrSel.m_nStartLine = nIndex + nCount;
   lrSel.m_nEndLine = nIndex + 1;
   LineList_SetSel(lpLL, &lrSel);
   OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

   CloseClipboard();

#endif      // ! OLE_VERSION
}


/* OutlineDoc_PasteOutlineData
 * ---------------------------
 *
 *      Put an array of Line Objects (stored in hOutline) into the document
 *
 * Return the number of items added
 */
int OutlineDoc_PasteOutlineData(LPOUTLINEDOC lpOutlineDoc, HGLOBAL hOutline, int nStartIndex)
{
   int nCount;
   int i;
   LPTEXTLINE arrLine;

   nCount = (int) GlobalSize(hOutline) / sizeof(TEXTLINE);
   arrLine = (LPTEXTLINE)GlobalLock(hOutline);
   if (!arrLine)
      return 0;

   for(i = 0; i < nCount; i++)
      Line_CopyToDoc((LPLINE)&arrLine[i], lpOutlineDoc, nStartIndex+i);

   GlobalUnlock(hOutline);

   return nCount;
}


/* OutlineDoc_PasteTextData
 * ------------------------
 *
 *      Build Line Objects from the strings (separated by '\n') in hText
 * and put them into the document
 */
int OutlineDoc_PasteTextData(LPOUTLINEDOC lpOutlineDoc, HGLOBAL hText, int nStartIndex)
{
   LPLINELIST  lpLL = (LPLINELIST)&lpOutlineDoc->m_LineList;
   HDC         hDC;
   LPSTR       lpszText;
   LPSTR       lpszEnd;
   LPTEXTLINE  lpLine;
   int         nLineCount;
   int         i;
   UINT        nTab;
   char        szBuf[MAXSTRLEN+1];

   lpszText=(LPSTR)GlobalLock(hText);
   if(!lpszText)
      return 0;

   lpszEnd = lpszText + lstrlen(lpszText);
   nLineCount=0;

   while(*lpszText && (lpszText<lpszEnd)) {

      // count the tab level
      nTab = 0;
      while((*lpszText == '\t') && (lpszText<lpszEnd)) {
         nTab++;
         lpszText++;
      }

      // collect the text string character by character
      for(i=0; (i<MAXSTRLEN) && (lpszText<lpszEnd); i++) {
         if ((! *lpszText) || (*lpszText == '\n'))
            break;
         szBuf[i] = *lpszText++;
      }
      szBuf[i] = 0;
      lpszText++;
      if ((i > 0) && (szBuf[i-1] == '\r'))
         szBuf[i-1] = 0;     // remove carriage return at the end

      hDC = LineList_GetDC(lpLL);
      lpLine = TextLine_Create(hDC, nTab, szBuf);
      LineList_ReleaseDC(lpLL, hDC);

      OutlineDoc_AddLine(
            lpOutlineDoc,
            (LPLINE)lpLine,
            nStartIndex + nLineCount
      );
      nLineCount++;

   }

   GlobalUnlock(hText);

   return nLineCount;
}


/* OutlineDoc_AddTextLineCommand
 * -----------------------------
 *
 *      Add a new text line following the current focus line.
 */
void OutlineDoc_AddTextLineCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   HDC hDC;
   int nIndex = LineList_GetFocusLineIndex(lpLL);
   char szBuf[MAXSTRLEN+1];
   UINT nTab = 0;
   LPLINE lpLine;
   LPTEXTLINE lpTextLine;

   szBuf[0] = '\0';

#if defined( USE_FRAMETOOLS )
   FrameTools_FB_GetEditText(
         lpOutlineDoc->m_lpFrameTools, szBuf, MAXSTRLEN+1);
#else
   if (! InputTextDlg(lpOutlineDoc->m_hWndDoc, szBuf, "Add Line"))
      return;
#endif

   hDC = LineList_GetDC(lpLL);
   lpLine = LineList_GetLine(lpLL, nIndex);
   if (lpLine)
      nTab = Line_GetTabLevel(lpLine);

   lpTextLine=TextLine_Create(hDC, nTab, szBuf);
   LineList_ReleaseDC(lpLL, hDC);

   if (! lpTextLine) {
      OutlineApp_ErrorMessage(lpOutlineApp, ErrOutOfMemory);
      return;
   }
   OutlineDoc_AddLine(lpOutlineDoc, (LPLINE)lpTextLine, nIndex);
}


/* OutlineDoc_AddTopLineCommand
 * ----------------------------
 *
 *      Add a top (margin) line as the first line in the LineList.
 *      (do not change the current selection)
 */
void OutlineDoc_AddTopLineCommand(
      LPOUTLINEDOC        lpOutlineDoc,
      UINT                nHeightInHimetric
)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPLINELIST  lpLL = &lpOutlineDoc->m_LineList;
   HDC         hDC = LineList_GetDC(lpLL);
   LPTEXTLINE  lpTextLine = TextLine_Create(hDC, 0, NULL);
   LPLINE      lpLine = (LPLINE)lpTextLine;
   LINERANGE   lrSel;
   int         nNumSel;

   LineList_ReleaseDC(lpLL, hDC);

   if (! lpTextLine) {
      OutlineApp_ErrorMessage(lpOutlineApp, ErrOutOfMemory);
      return;
   }

   Line_SetHeightInHimetric(lpLine, nHeightInHimetric);

   nNumSel=LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);
   if (nNumSel > 0) {
      // adjust current selection to keep equivalent selection
      lrSel.m_nStartLine += 1;
      lrSel.m_nEndLine += 1;
   }
   OutlineDoc_AddLine(lpOutlineDoc, lpLine, -1);
   if (nNumSel > 0)
      LineList_SetSel(lpLL, (LPLINERANGE)&lrSel);
}


#if defined( USE_FRAMETOOLS )


/* OutlineDoc_SetFormulaBarEditText
 * --------------------------------
 *
 *      Fill the edit control in the formula with the text string from a
 * TextLine in focus.
 */
void OutlineDoc_SetFormulaBarEditText(
      LPOUTLINEDOC            lpOutlineDoc,
      LPLINE                  lpLine
)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   char cBuf[MAXSTRLEN+1];

   if (! lpOutlineDoc || ! lpOutlineDoc->m_lpFrameTools)
      return;

   if (Line_GetLineType(lpLine) != TEXTLINETYPE) {
      FrameTools_FB_SetEditText(lpOutlineDoc->m_lpFrameTools, NULL);
   } else {
      TextLine_GetTextData((LPTEXTLINE)lpLine, (LPSTR)cBuf);
      FrameTools_FB_SetEditText(lpOutlineDoc->m_lpFrameTools, (LPSTR)cBuf);
   }
}


/* OutlineDoc_SetFormulaBarEditFocus
 * ---------------------------------
 *
 *  Setup for formula bar to gain or loose edit focus.
 *  if gaining focus, setup up special accelerator table and scroll line
 *      into view.
 *  else restore normal accelerator table.
 */
void OutlineDoc_SetFormulaBarEditFocus(
      LPOUTLINEDOC            lpOutlineDoc,
      BOOL                    fEditFocus
)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPLINELIST lpLL;
   int nFocusIndex;

   if (! lpOutlineDoc || ! lpOutlineDoc->m_lpFrameTools)
      return;

   lpOutlineDoc->m_lpFrameTools->m_fInFormulaBar = fEditFocus;

   if (fEditFocus && lpOutlineDoc->m_lpFrameTools) {
      lpLL = OutlineDoc_GetLineList(lpOutlineDoc);

      nFocusIndex = LineList_GetFocusLineIndex(lpLL);
      LineList_ScrollLineIntoView(lpLL, nFocusIndex);
      FrameTools_FB_FocusEdit(lpOutlineDoc->m_lpFrameTools);
   }

   OutlineApp_SetFormulaBarAccel(lpOutlineApp, fEditFocus);
}


/* OutlineDoc_IsEditFocusInFormulaBar
** ----------------------------------
**    Returns TRUE if edit focus is currently in the formula bar
**    else FALSE if not.
*/
BOOL OutlineDoc_IsEditFocusInFormulaBar(LPOUTLINEDOC lpOutlineDoc)
{
   if (! lpOutlineDoc || ! lpOutlineDoc->m_lpFrameTools)
      return FALSE;

   return lpOutlineDoc->m_lpFrameTools->m_fInFormulaBar;
}


/* OutlineDoc_UpdateFrameToolButtons
** ---------------------------------
**    Update the Enable/Disable states of the buttons in the formula
**    bar and button bar.
*/
void OutlineDoc_UpdateFrameToolButtons(LPOUTLINEDOC lpOutlineDoc)
{
   if (! lpOutlineDoc || ! lpOutlineDoc->m_lpFrameTools)
      return;
   FrameTools_UpdateButtons(lpOutlineDoc->m_lpFrameTools, lpOutlineDoc);
}
#endif  // USE_FRAMETOOLS


/* OutlineDoc_EditLineCommand
 * --------------------------
 *
 *      Edit the current focus line.
 */
void OutlineDoc_EditLineCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   HDC hDC = LineList_GetDC(lpLL);
   int nIndex = LineList_GetFocusLineIndex(lpLL);
   LPLINE lpLine = LineList_GetLine(lpLL, nIndex);
   int nOrgLineWidthInHimetric;
   int nNewLineWidthInHimetric;
   BOOL fSizeChanged;

   if (!lpLine)
      return;

   nOrgLineWidthInHimetric = Line_GetTotalWidthInHimetric(lpLine);
   if (Line_Edit(lpLine, lpOutlineDoc->m_hWndDoc, hDC)) {
      nNewLineWidthInHimetric = Line_GetTotalWidthInHimetric(lpLine);

      if (nNewLineWidthInHimetric > nOrgLineWidthInHimetric) {
         fSizeChanged = LineList_SetMaxLineWidthInHimetric(
               lpLL,
               nNewLineWidthInHimetric
            );
      } else {
         fSizeChanged = LineList_RecalcMaxLineWidthInHimetric(
               lpLL,
               nOrgLineWidthInHimetric
            );
      }

#if defined( OLE_SERVER )
      /* Update Name Table */
      ServerNameTable_EditLineUpdate(
            (LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable,
            nIndex
      );
#endif

      OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, fSizeChanged);

      LineList_ForceLineRedraw(lpLL, nIndex, TRUE);
   }
   LineList_ReleaseDC(lpLL, hDC);
}


/* OutlineDoc_IndentCommand
 * ------------------------
 *
 *      Indent selection of lines
 */
void OutlineDoc_IndentCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST  lpLL = &lpOutlineDoc->m_LineList;
   LPLINE      lpLine;
   HDC         hDC = LineList_GetDC(lpLL);
   int         i;
   int         nIndex;
   int         nNumSel;
   LINERANGE   lrSel;
   BOOL        fSizeChanged = FALSE;

   nNumSel=LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);

   OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );

   for(i = 0; i < nNumSel; i++) {
      nIndex = lrSel.m_nStartLine + i;
      lpLine=LineList_GetLine(lpLL, nIndex);
      if (! lpLine)
         continue;

      Line_Indent(lpLine, hDC);
      if (LineList_SetMaxLineWidthInHimetric(lpLL,
         Line_GetTotalWidthInHimetric(lpLine))) {
         fSizeChanged = TRUE;
      }
      LineList_ForceLineRedraw(lpLL, nIndex, TRUE);

#if defined( OLE_SERVER )
      /* Update Name Table */
      ServerNameTable_EditLineUpdate(
            (LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable,
            nIndex
      );
#endif

   }

   LineList_ReleaseDC(lpLL, hDC);

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, fSizeChanged);
   OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );
}


/* OutlineDoc_UnindentCommand
 * --------------------------
 *
 *      Unindent selection of lines
 */
void OutlineDoc_UnindentCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST  lpLL = &lpOutlineDoc->m_LineList;
   LPLINE      lpLine;
   HDC         hDC = LineList_GetDC(lpLL);
   int         nOrgLineWidthInHimetric;
   int         nOrgMaxLineWidthInHimetric = 0;
   int         i;
   int         nIndex;
   int         nNumSel;
   LINERANGE   lrSel;
   BOOL        fSizeChanged;

   nNumSel=LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);

   OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );

   for(i = 0; i < nNumSel; i++) {
      nIndex = lrSel.m_nStartLine + i;
      lpLine=LineList_GetLine(lpLL, nIndex);
      if (!lpLine)
         continue;

      nOrgLineWidthInHimetric = Line_GetTotalWidthInHimetric(lpLine);
      nOrgMaxLineWidthInHimetric =
            (nOrgLineWidthInHimetric > nOrgMaxLineWidthInHimetric ?
               nOrgLineWidthInHimetric : nOrgMaxLineWidthInHimetric);
      Line_Unindent(lpLine, hDC);
      LineList_ForceLineRedraw(lpLL, nIndex, TRUE);

#if defined( OLE_SERVER )
      /* Update Name Table */
      ServerNameTable_EditLineUpdate(
            (LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable,
            nIndex
      );
#endif

   }

   LineList_ReleaseDC(lpLL, hDC);

   fSizeChanged = LineList_RecalcMaxLineWidthInHimetric(
         lpLL,
         nOrgMaxLineWidthInHimetric
      );

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, fSizeChanged);
   OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );
}


/* OutlineDoc_SetLineHeightCommand
 * -------------------------------
 *
 *      Set height of the selection of lines
 */
void OutlineDoc_SetLineHeightCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPLINELIST  lpLL;
   HDC         hDC;
   LPLINE      lpLine;
   int         nNewHeight;
   int         i;
   int         nIndex;
   int         nNumSel;
   LINERANGE   lrSel;

   if (!lpOutlineDoc)
      return;

   lpLL = &lpOutlineDoc->m_LineList;
   nNumSel=LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);
   lpLine = LineList_GetLine(lpLL, lrSel.m_nStartLine);
   if (!lpLine)
      return;

   nNewHeight = Line_GetHeightInHimetric(lpLine);

#if defined( OLE_VERSION )
   OleApp_PreModalDialog((LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineDoc);
#endif

   DialogBoxParam(
         lpOutlineApp->m_hInst,
         (LPSTR)"SetLineHeight",
         lpOutlineDoc->m_hWndDoc,
         (DLGPROC)SetLineHeightDlgProc,
         (LPARAM)(LPINT)&nNewHeight
   );

#if defined( OLE_VERSION )
   OleApp_PostModalDialog((LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineDoc);
#endif

   if (nNewHeight == 0)
      return;     /* user hit cancel */

   hDC = LineList_GetDC(lpLL);

   for (i = 0; i < nNumSel; i++) {
      nIndex = lrSel.m_nStartLine + i;
      lpLine=LineList_GetLine(lpLL, nIndex);
      if (nNewHeight == -1) {
         switch (Line_GetLineType(lpLine)) {

            case TEXTLINETYPE:

               TextLine_CalcExtents((LPTEXTLINE)lpLine, hDC);
               break;

#if defined( OLE_CNTR )
            case CONTAINERLINETYPE:

               ContainerLine_SetHeightInHimetric(
                     (LPCONTAINERLINE)lpLine, -1);
               break;
#endif

         }
      }
      else
         Line_SetHeightInHimetric(lpLine, nNewHeight);


      LineList_SetLineHeight(lpLL, nIndex,
            Line_GetHeightInHimetric(lpLine));
   }

   LineList_ReleaseDC(lpLL, hDC);

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, TRUE);
   LineList_ForceRedraw(lpLL, TRUE);
}



/* OutlineDoc_SelectAllCommand
 * ---------------------------
 *
 *      Select all the lines in the document.
 */
void OutlineDoc_SelectAllCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   LINERANGE lrSel;

   lrSel.m_nStartLine = 0;
   lrSel.m_nEndLine = LineList_GetCount(lpLL) - 1;
   LineList_SetSel(lpLL, &lrSel);
}


/* OutlineDoc_DefineNameCommand
 * ----------------------------
 *
 *      Define a name in the document
 */
void OutlineDoc_DefineNameCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

#if defined( OLE_VERSION )
   OleApp_PreModalDialog((LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineDoc);
#endif

   DialogBoxParam(
         lpOutlineApp->m_hInst,
         (LPSTR)"DefineName",
         lpOutlineDoc->m_hWndDoc,
         (DLGPROC)DefineNameDlgProc,
         (LPARAM) lpOutlineDoc
   );

#if defined( OLE_VERSION )
   OleApp_PostModalDialog((LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineDoc);
#endif
}


/* OutlineDoc_GotoNameCommand
 * --------------------------
 *
 *      Goto a predefined name in the document
 */
void OutlineDoc_GotoNameCommand(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

#if defined( OLE_VERSION )
   OleApp_PreModalDialog((LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineDoc);
#endif

   DialogBoxParam(
         lpOutlineApp->m_hInst,
         (LPSTR)"GotoName",
         lpOutlineDoc->m_hWndDoc,
         (DLGPROC)GotoNameDlgProc,
         (LPARAM)lpOutlineDoc
   );

#if defined( OLE_VERSION )
   OleApp_PostModalDialog((LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineDoc);
#endif
}


/* OutlineDoc_ShowWindow
 * ---------------------
 *
 *      Show the window of the document to the user.
 */
void OutlineDoc_ShowWindow(LPOUTLINEDOC lpOutlineDoc)
{
#if defined( _DEBUG )
   OleDbgAssertSz(lpOutlineDoc->m_docInitType != DOCTYPE_UNKNOWN,
            "OutlineDoc_ShowWindow: can't show unitialized document\r\n");
#endif
   if (lpOutlineDoc->m_docInitType == DOCTYPE_UNKNOWN)
      return;

#if defined( OLE_VERSION )
   // Call OLE version of this function instead
   OleDoc_ShowWindow((LPOLEDOC)lpOutlineDoc);
#else
   ShowWindow(lpOutlineDoc->m_hWndDoc, SW_SHOWNORMAL);
   SetForegroundWindow(lpOutlineDoc->m_hWndDoc);
#endif
}


#if defined( USE_FRAMETOOLS )

void OutlineDoc_AddFrameLevelTools(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
#if defined( INPLACE_CNTR )
   // Call OLE In-Place Container version of this function instead
   ContainerDoc_AddFrameLevelTools((LPCONTAINERDOC)lpOutlineDoc);

#else   // ! INPLACE_CNTR
   RECT rcFrameRect;
   BORDERWIDTHS frameToolWidths;

#if defined( INPLACE_SVR )
   LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;
   LPOLEINPLACEFRAME lpTopIPFrame=ServerDoc_GetTopInPlaceFrame(lpServerDoc);

   // if in-place active, add our tools to our in-place container's frame.
   if (lpTopIPFrame) {
      ServerDoc_AddFrameLevelTools(lpServerDoc);
      return;
   }
#endif  // INPLACE_SVR

   OutlineApp_GetFrameRect(g_lpApp, (LPRECT)&rcFrameRect);
   FrameTools_GetRequiredBorderSpace(
         lpOutlineDoc->m_lpFrameTools,
         (LPBORDERWIDTHS)&frameToolWidths
   );
   OutlineApp_SetBorderSpace(g_lpApp, (LPBORDERWIDTHS)&frameToolWidths);
   FrameTools_AttachToFrame(
         lpOutlineDoc->m_lpFrameTools, OutlineApp_GetWindow(lpOutlineApp));
   FrameTools_Move(lpOutlineDoc->m_lpFrameTools, (LPRECT)&rcFrameRect);
#endif  // ! INPLACE_CNTR

}

#endif  // USE_FRAMETOOLS


/* OutlineDoc_GetWindow
 * --------------------
 *
 *      Get the window handle of the document.
 */
HWND OutlineDoc_GetWindow(LPOUTLINEDOC lpOutlineDoc)
{
   if(! lpOutlineDoc) return NULL;
   return lpOutlineDoc->m_hWndDoc;
}


/* OutlineDoc_AddLine
 * ------------------
 *
 *      Add one line to the Document's LineList
 */
void OutlineDoc_AddLine(LPOUTLINEDOC lpOutlineDoc, LPLINE lpLine, int nIndex)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;

   LineList_AddLine(lpLL, lpLine, nIndex);

   /* Update Name Table */
   OutlineNameTable_AddLineUpdate(lpOutlineDoc->m_lpNameTable, nIndex);

#if defined( INPLACE_CNTR )
   {
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;
      /* OLE2NOTE: after adding a line we need to
      **    update the PosRect of the In-Place active
      **    objects (if any) that follow the added line.
      **    NOTE: nIndex is index of line before new line.
      **          nIndex+1 is index of new line
      **          nIndex+2 is index of line after new line.
      */
      ContainerDoc_UpdateInPlaceObjectRects(lpContainerDoc, nIndex+2);
   }
#endif

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, TRUE);
}


/* OutlineDoc_DeleteLine
 * ---------------------
 *
 *
 *      Delete one line from the document's LineList
 */
void OutlineDoc_DeleteLine(LPOUTLINEDOC lpOutlineDoc, int nIndex)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;

#if defined( OLE_CNTR )
   LPLINE lpLine = LineList_GetLine(lpLL, nIndex);
   LPSTORAGE lpStgDoc = NULL;
   OLECHAR szSaveStgName[CWCSTORAGENAME];
   BOOL fDeleteChildStg = FALSE;

   if (lpLine && (Line_GetLineType(lpLine) == CONTAINERLINETYPE) ) {

      /* OLE2NOTE: when a ContainerLine is being deleted by the user,
      **    it is important to delete the object's sub-storage
      **    otherwise it wastes space in the ContainerDoc's file.
      **    this function is called when lines are deleted by the
      **    Clear command and when lines are deleted by a DRAGMOVE
      **    operation.
      */
      LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;

      // save name of child storage
//      LSTRCPYN(szSaveStgName, lpContainerLine->m_szStgName,sizeof(szSaveStgName));
      OLESTRCPY(szSaveStgName, lpContainerLine->m_szStgName);
      lpStgDoc = ((LPOLEDOC)lpContainerLine->m_lpDoc)->m_lpStg;
      fDeleteChildStg = TRUE;
   }
#endif  // OLE_CNTR

   LineList_DeleteLine(lpLL, nIndex);

#if defined( OLE_CNTR )
   if (fDeleteChildStg && lpStgDoc) {
      HRESULT hrErr;

      // delete the obsolete child storage. it is NOT fatal if this fails

      hrErr = lpStgDoc->lpVtbl->DestroyElement(lpStgDoc, szSaveStgName);

#if defined( _DEBUG )
      if (hrErr != NOERROR) {
         OleDbgOutHResult("IStorage::DestroyElement return", hrErr);
      }
#endif
   }
#endif  // OLE_CNTR

   /* Update Name Table */
   OutlineNameTable_DeleteLineUpdate(lpOutlineDoc->m_lpNameTable, nIndex);

#if defined( INPLACE_CNTR )
   {
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;
      /* OLE2NOTE: after deleting a line we need to
      **    update the PosRect of the In-Place active
      **    objects (if any).
      */
      ContainerDoc_UpdateInPlaceObjectRects(lpContainerDoc, nIndex);
   }
#endif

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, TRUE);

#if defined( OLE_VERSION )
   {
      LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
      LPOLEDOC    lpClipboardDoc = (LPOLEDOC)lpOutlineApp->m_lpClipboardDoc;

      /* OLE2NOTE: if the document that is the source of data on the
      **    clipborad has just had lines deleted, then the copied data
      **    is no longer considered a valid potential link source.
      **    disable the offering of CF_LINKSOURCE from the clipboard
      **    document. this avoids problems that arise when the
      **    editing operation changes or deletes the original data
      **    copied. we will not go to the trouble of determining if
      **    the deleted line actually is part of the link source.
      */
      if (lpClipboardDoc
         && lpClipboardDoc->m_fLinkSourceAvail
         && lpClipboardDoc->m_lpSrcDocOfCopy == (LPOLEDOC)lpOutlineDoc) {
         lpClipboardDoc->m_fLinkSourceAvail = FALSE;

         /* OLE2NOTE: since we are changing the list of formats on
         **    the clipboard (ie. removing CF_LINKSOURCE), we must
         **    call OleSetClipboard again. to be sure that the
         **    clipboard datatransfer document object does not get
         **    destroyed we will guard the call to OleSetClipboard
         **    within a pair of AddRef/Release.
         */
         OleDoc_AddRef((LPOLEDOC)lpClipboardDoc);    // guard obj life-time

         OLEDBG_BEGIN2("OleSetClipboard called\r\n")
         OleSetClipboard(
               (LPDATAOBJECT)&((LPOLEDOC)lpClipboardDoc)->m_DataObject);
         OLEDBG_END2

         OleDoc_Release((LPOLEDOC)lpClipboardDoc);    // rel. AddRef above
      }
   }
#endif  // OLE_VERSION
}


/* OutlineDoc_AddName
 * ------------------
 *
 *      Add a Name to the Document's NameTable
 */
void OutlineDoc_AddName(LPOUTLINEDOC lpOutlineDoc, LPOUTLINENAME lpOutlineName)
{
   LPOUTLINENAMETABLE lpOutlineNameTable = lpOutlineDoc->m_lpNameTable;

   OutlineNameTable_AddName(lpOutlineNameTable, lpOutlineName);

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, FALSE, FALSE);
}


/* OutlineDoc_DeleteName
 * ---------------------
 *
 *
 *      Delete Name from the document's NameTable
 */
void OutlineDoc_DeleteName(LPOUTLINEDOC lpOutlineDoc, int nIndex)
{
   LPOUTLINENAMETABLE lpOutlineNameTable = lpOutlineDoc->m_lpNameTable;

   OutlineNameTable_DeleteName(lpOutlineNameTable, nIndex);

   OutlineDoc_SetModified(lpOutlineDoc, TRUE, FALSE, FALSE);
}


/* OutlineDoc_Destroy
 * ------------------
 *
 *  Free all memory that had been allocated for a document.
 *      this destroys the LineList & NameTable of the document.
 */
void OutlineDoc_Destroy(LPOUTLINEDOC lpOutlineDoc)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
#if defined( OLE_VERSION )
   LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
   LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineDoc;

   if (lpOleDoc->m_fObjIsDestroying)
      return;     // doc destruction is in progress
#endif  // OLE_VERSION

   OLEDBG_BEGIN3("OutlineDoc_Destroy\r\n");

#if defined( OLE_VERSION )

   /* OLE2NOTE: in order to guarantee that the application does not
   **    prematurely exit before the destruction of the document is
   **    complete, we intially AddRef the App refcnt later Release it.
   **    This initial AddRef is artificial; it simply guarantees that
   **    the app object does not get destroyed until the end of this
   **    routine.
   */
   OleApp_AddRef(lpOleApp);

   /* OLE2NOTE: perform processing required for OLE */
   OleDoc_Destroy(lpOleDoc);
#endif

   LineList_Destroy(lpLL);
   OutlineNameTable_Destroy(lpOutlineDoc->m_lpNameTable);

#if defined( USE_HEADING )
   if (! lpOutlineDoc->m_fDataTransferDoc)
      Heading_Destroy((LPHEADING)&lpOutlineDoc->m_heading);
#endif

#if defined( USE_FRAMETOOLS )
   if (! lpOutlineDoc->m_fDataTransferDoc)
      FrameTools_AssociateDoc(lpOutlineDoc->m_lpFrameTools, NULL);
#endif  // USE_FRAMETOOLS

   DestroyWindow(lpOutlineDoc->m_hWndDoc);
   Delete(lpOutlineDoc);   // free memory for doc itself
   OleDbgOut1("@@@@ DOC DESTROYED\r\n");

#if defined( OLE_VERSION )
   OleApp_Release(lpOleApp);       // release artificial AddRef above
#endif

   OLEDBG_END3
}


/* OutlineDoc_ReSize
 * -----------------
 *
 *  Resize the document and its components
 *
 * Parameter:
 *      lpRect  the new size of the document. Use current size if NULL
 */
void OutlineDoc_Resize(LPOUTLINEDOC lpOutlineDoc, LPRECT lpRect)
{
   RECT            rect;
   LPLINELIST      lpLL;

#if defined( USE_HEADING )
   LPHEADING       lphead;
#endif  // USE_HEADING

   LPSCALEFACTOR   lpscale;
   HWND            hWndLL;

   if (!lpOutlineDoc)
      return;

   lpLL = (LPLINELIST)&lpOutlineDoc->m_LineList;
   lpscale = (LPSCALEFACTOR)&lpOutlineDoc->m_scale;
   hWndLL = LineList_GetWindow(lpLL);

   if (lpRect) {
      CopyRect((LPRECT)&rect, lpRect);
      MoveWindow(lpOutlineDoc->m_hWndDoc, rect.left, rect.top,
            rect.right-rect.left, rect.bottom-rect.top, TRUE);
   }

   GetClientRect(lpOutlineDoc->m_hWndDoc, (LPRECT)&rect);

#if defined( USE_HEADING )
   lphead = OutlineDoc_GetHeading(lpOutlineDoc);
   rect.left += Heading_RH_GetWidth(lphead, lpscale);
   rect.top += Heading_CH_GetHeight(lphead, lpscale);
#endif  // USE_HEADING

   if (lpLL) {
      MoveWindow(hWndLL, rect.left, rect.top,
            rect.right-rect.left, rect.bottom-rect.top, TRUE);
   }

#if defined( USE_HEADING )
   if (lphead)
      Heading_Move(lphead, lpOutlineDoc->m_hWndDoc, lpscale);
#endif  // USE_HEADING

#if defined( INPLACE_CNTR )
   ContainerDoc_UpdateInPlaceObjectRects((LPCONTAINERDOC)lpOutlineDoc, 0);
#endif
}


/* OutlineDoc_GetNameTable
 * -----------------------
 *
 *      Get nametable associated with the line list
 */
LPOUTLINENAMETABLE OutlineDoc_GetNameTable(LPOUTLINEDOC lpOutlineDoc)
{
   if (!lpOutlineDoc)
      return NULL;
   else
      return lpOutlineDoc->m_lpNameTable;
}


/* OutlineDoc_GetLineList
 * ----------------------
 *
 *      Get listlist associated with the OutlineDoc
 */
LPLINELIST OutlineDoc_GetLineList(LPOUTLINEDOC lpOutlineDoc)
{
   if (!lpOutlineDoc)
      return NULL;
   else
      return (LPLINELIST)&lpOutlineDoc->m_LineList;
}


/* OutlineDoc_GetNameCount
 * -----------------------
 *
 * Return number of names in table
 */
int OutlineDoc_GetNameCount(LPOUTLINEDOC lpOutlineDoc)
{
   return OutlineNameTable_GetCount(lpOutlineDoc->m_lpNameTable);
}


/* OutlineDoc_GetLineCount
 * -----------------------
 *
 * Return number of lines in the LineList
 */
int OutlineDoc_GetLineCount(LPOUTLINEDOC lpOutlineDoc)
{
   return LineList_GetCount(&lpOutlineDoc->m_LineList);
}


/* OutlineDoc_SetFileName
 * ----------------------
 *
 *      Set the filename of a document.
 *
 *  OLE2NOTE: If the ServerDoc has a valid filename then, the object is
 *  registered in the running object table (ROT). if the name of the doc
 *  changes (eg. via SaveAs) then the previous registration must be revoked
 *  and the document re-registered under the new name.
 */
BOOL OutlineDoc_SetFileName(LPOUTLINEDOC lpOutlineDoc, LPOLESTR lpszNewFileName, LPSTORAGE lpNewStg)
{
   OleDbgAssertSz(lpszNewFileName != NULL,   "Can't reset doc to Untitled!");
   if (lpszNewFileName == NULL)
      return FALSE;

//   AnsiLowerBuff(lpszNewFileName, (UINT)OLESTRLEN(lpszNewFileName));
   CharLowerBuffW(lpszNewFileName, OLESTRLEN(lpszNewFileName));

#if defined( OLE_CNTR )
   {
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;
      LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineDoc;

      /* OLE2NOTE: the container version of the application keeps its
      **    storage open at all times. if the document's storage is not
      **    open, then open it.
      */

      if (lpNewStg) {

         /* CASE 1 -- document is being loaded from a file. lpNewStg is
         **    still open from the OutlineDoc_LoadFromFile function.
         */

         lpOutlineDoc->m_docInitType = DOCTYPE_FROMFILE;

      } else {

         /* CASE 2 -- document is being associated with a valid file
         **    that is not yet open. thus we must now open the file.
         */

         if (lpOutlineDoc->m_docInitType == DOCTYPE_FROMFILE &&
               OLESTRCMP(lpOutlineDoc->m_szFileName,lpszNewFileName)==0) {

            /* CASE 2a -- new filename is same as current file. if the
            **    stg is already open, then the lpStg is still valid.
            **    if it is not open, then open it.
            */
            if (! lpOleDoc->m_lpStg) {
               lpOleDoc->m_lpStg = OleStdOpenRootStorage(
                     lpszNewFileName,
                     STGM_READWRITE | STGM_SHARE_DENY_WRITE
               );
               if (! lpOleDoc->m_lpStg) return FALSE;
            }

         } else {

            /* CASE 2b -- new filename is NOT same as current file.
            **    a SaveAs operation is pending. open the new file and
            **    hold the storage pointer in m_lpNewStg. the
            **    subsequent call to Doc_SaveToFile will save the
            **    document into the new storage pointer and release the
            **    old storage pointer.
            */

            lpOutlineDoc->m_docInitType = DOCTYPE_FROMFILE;

            lpContainerDoc->m_lpNewStg = OleStdCreateRootStorage(
                  lpszNewFileName,
                  STGM_READWRITE | STGM_SHARE_DENY_WRITE | STGM_CREATE
            );
            if (! lpContainerDoc->m_lpNewStg) return FALSE;
         }
      }
   }
#endif      // OLE_CNTR

   if (lpOutlineDoc->m_docInitType != DOCTYPE_FROMFILE ||
      OLESTRCMP(lpOutlineDoc->m_szFileName, lpszNewFileName) != 0) {

      /* A new valid file name is being associated with the document */

      OLESTRCPY(lpOutlineDoc->m_szFileName, lpszNewFileName);
      lpOutlineDoc->m_docInitType = DOCTYPE_FROMFILE;

      // set lpszDocTitle to point to filename without path
      lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName +
         OLESTRLEN(lpOutlineDoc->m_szFileName) - 1;
      while (lpOutlineDoc->m_lpszDocTitle > lpOutlineDoc->m_szFileName
         && ! IS_FILENAME_DELIM(lpOutlineDoc->m_lpszDocTitle[-1])) {
         lpOutlineDoc->m_lpszDocTitle--;
      }

      OutlineDoc_SetTitle(lpOutlineDoc, TRUE /*fMakeUpperCase*/);

#if defined( OLE_VERSION )
      {
         /* OLE2NOTE: both containers and servers must properly
         **    register in the RunningObjectTable. if the document
         **    is performing a SaveAs operation, then it must
         **    re-register in the ROT with the new moniker. in
         **    addition any embedded object, pseudo objects, and/or
         **    linking clients must be informed that the document's
         **    moniker has changed.
         */

         LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineDoc;

         if (lpOleDoc->m_lpFileMoniker) {
            OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpFileMoniker);
            lpOleDoc->m_lpFileMoniker = NULL;
         }

         CreateFileMoniker(lpszNewFileName,&lpOleDoc->m_lpFileMoniker);
         OleDoc_DocRenamedUpdate(lpOleDoc, lpOleDoc->m_lpFileMoniker);
      }
#endif      // OLE_VERSION

   }

   return TRUE;
}


/* OutlineDoc_SetTitle
 * -------------------
 *
 * Set window text to be current filename.
 * The following window hierarchy exits:
 *      hWndApp
 *          hWndDoc
 *              hWndListBox
 *  The frame window is the window which gets the title.
 */
void OutlineDoc_SetTitle(LPOUTLINEDOC lpOutlineDoc, BOOL fMakeUpperCase)
{
   HWND hWnd;
   LPSTR lpszText;
   char  szAnsiStr[256];

   if (!lpOutlineDoc->m_hWndDoc) return;
   if ((hWnd = GetParent(lpOutlineDoc->m_hWndDoc)) == NULL) return;

   lpszText = OleStdMalloc((UINT)(OLESTRLEN(APPNAME) + 4 +
                           OLESTRLEN(lpOutlineDoc->m_lpszDocTitle)));
   if (!lpszText) return;

   W2A(APPNAME, lpszText, OLESTRLEN(APPNAME)+1);
   lstrcat(lpszText," - ");
   W2A (lpOutlineDoc->m_lpszDocTitle, szAnsiStr, 256);
   lstrcat(lpszText, (LPSTR)szAnsiStr);

   if (fMakeUpperCase)
      AnsiUpperBuff(lpszText, (UINT)lstrlen(lpszText));

   SetWindowText(hWnd,lpszText);
   OleStdFree(lpszText);
}


/* OutlineDoc_Close
 * ----------------
 *
 * Close active document. If modified, prompt the user if
 * he wants to save.
 *
 *  Returns:
 *      FALSE -- user canceled the closing of the doc.
 *      TRUE -- the doc was successfully closed
 */
BOOL OutlineDoc_Close(LPOUTLINEDOC lpOutlineDoc, DWORD dwSaveOption)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

#if defined( OLE_VERSION )
   /* OLE2NOTE: call OLE specific function instead */
   return OleDoc_Close((LPOLEDOC)lpOutlineDoc, dwSaveOption);

#else

   if (! lpOutlineDoc)
      return TRUE;            // active doc's are already destroyed

   if (! OutlineDoc_CheckSaveChanges(lpOutlineDoc, &dwSaveOption))
      return FALSE;           // abort closing the doc

   OutlineDoc_Destroy(lpOutlineDoc);

   OutlineApp_DocUnlockApp(lpOutlineApp, lpOutlineDoc);

   return TRUE;

#endif      // ! OLE_VERSION
}


/* OutlineDoc_CheckSaveChanges
 * ---------------------------
 *
 * Check if the document has been modified. if so, prompt the user if
 *      the changes should be saved. if yes save them.
 * Returns TRUE if the doc is safe to close (user answered Yes or No)
 *         FALSE if the user canceled the save changes option.
 */
BOOL OutlineDoc_CheckSaveChanges(
      LPOUTLINEDOC        lpOutlineDoc,
      LPDWORD             lpdwSaveOption
)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   int nResponse;
   char szAnsiString[256];
   char szAnsiString2[256];

   if (*lpdwSaveOption == OLECLOSE_NOSAVE)
      return TRUE;

   if(! OutlineDoc_IsModified(lpOutlineDoc))
      return TRUE;    // saving is not necessary

   /* OLE2NOTE: our document is dirty so it needs to be saved. if
   **    OLECLOSE_PROMPTSAVE the user should be prompted to see if the
   **    document should be saved. is specified but the document is NOT
   **    visible to the user, then the user can NOT be prompted. in
   **    the situation the document should be saved without prompting.
   **    if OLECLOSE_SAVEIFDIRTY is specified then, the document
   **    should also be saved without prompting.
   */
   if (*lpdwSaveOption == OLECLOSE_PROMPTSAVE &&
         IsWindowVisible(lpOutlineDoc->m_hWndDoc)) {

      // prompt the user to see if changes should be saved.
#if defined( OLE_VERSION )
      OleApp_PreModalDialog(
            (LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineApp->m_lpDoc);
#endif
      W2A (APPNAME, szAnsiString, 256);
      W2A (MsgSaveFile, szAnsiString2, 256);
      nResponse = MessageBox(
            lpOutlineApp->m_hWndApp,
            szAnsiString2,
            szAnsiString,
            MB_ICONQUESTION | MB_YESNOCANCEL
      );
#if defined( OLE_VERSION )
      OleApp_PostModalDialog(
            (LPOLEAPP)lpOutlineApp, (LPOLEDOC)lpOutlineApp->m_lpDoc);
#endif
      if(nResponse==IDCANCEL)
         return FALSE;   // close is canceled
      if(nResponse==IDNO) {
         // Reset the save option to NOSAVE per user choice
         *lpdwSaveOption = OLECLOSE_NOSAVE;
         return TRUE;    // don't save, but is ok to close
      }
   } else if (*lpdwSaveOption != OLECLOSE_SAVEIFDIRTY) {
      OleDbgAssertSz(FALSE, "Invalid dwSaveOption\r\n");
      *lpdwSaveOption = OLECLOSE_NOSAVE;
      return TRUE;        // unknown *lpdwSaveOption; close w/o saving
   }

#if defined( OLE_SERVER )
   if (lpOutlineDoc->m_docInitType == DOCTYPE_EMBEDDED) {
      LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;
      HRESULT hrErr;

      /* OLE2NOTE: Update the container before closing without prompting
      **    the user. To update the container, we must ask our container
      **    to save us.
      */
      OleDbgAssert(lpServerDoc->m_lpOleClientSite != NULL);
      OLEDBG_BEGIN2("IOleClientSite::SaveObject called\r\n")
      hrErr = lpServerDoc->m_lpOleClientSite->lpVtbl->SaveObject(
            lpServerDoc->m_lpOleClientSite
      );
      OLEDBG_END2

      if (hrErr != NOERROR) {
         OleDbgOutHResult("IOleClientSite::SaveObject returned", hrErr);
         return FALSE;
      }

      return TRUE;    // doc is safe to be closed

   } else
#endif      // OLE_SERVER
   {
      return OutlineApp_SaveCommand(lpOutlineApp);
   }
}


/* OutlineDoc_IsModified
 * ---------------------
 *
 * Return modify flag of OUTLINEDOC
 */
BOOL OutlineDoc_IsModified(LPOUTLINEDOC lpOutlineDoc)
{
   if (lpOutlineDoc->m_fModified)
      return lpOutlineDoc->m_fModified;

#if defined( OLE_CNTR )
   {
      /* OLE2NOTE: if there are OLE objects, then we must ask if any of
      **    them are dirty. if so we must consider our document
      **    as modified.
      */
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;
      LPLINELIST  lpLL;
      int         nLines;
      int         nIndex;
      LPLINE      lpLine;
      HRESULT     hrErr;

      lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
      nLines = LineList_GetCount(lpLL);

      for (nIndex = 0; nIndex < nLines; nIndex++) {
         lpLine = LineList_GetLine(lpLL, nIndex);
         if (!lpLine)
            break;
         if (Line_GetLineType(lpLine) == CONTAINERLINETYPE) {
            LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
            if (lpContainerLine->m_lpPersistStg) {
               hrErr = lpContainerLine->m_lpPersistStg->lpVtbl->IsDirty(
                     lpContainerLine->m_lpPersistStg);
               /* OLE2NOTE: we will only accept an explicit "no i
               **    am NOT dirty statement" (ie. S_FALSE) as an
               **    indication that the object is clean. eg. if
               **    the object returns E_NOTIMPL we must
               **    interpret it as the object IS dirty.
               */
               if (GetScode(hrErr) != S_FALSE)
                  return TRUE;
            }
         }
      }
   }
#endif
   return FALSE;
}


/* OutlineDoc_SetModified
 * ----------------------
 *
 *  Set the modified flag of the document
 *
 */
void OutlineDoc_SetModified(LPOUTLINEDOC lpOutlineDoc, BOOL fModified, BOOL fDataChanged, BOOL fSizeChanged)
{
   lpOutlineDoc->m_fModified = fModified;

#if defined( OLE_SERVER )
   if (! lpOutlineDoc->m_fDataTransferDoc) {
      LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;

      /* OLE2NOTE: if the document has changed, then broadcast the change
      **    to all clients who have set up Advise connections. notify
      **    them that our data (and possibly also our extents) have
      **    changed.
      */
      if (fDataChanged) {
         lpServerDoc->m_fDataChanged     = TRUE;
         lpServerDoc->m_fSizeChanged     = fSizeChanged;
         lpServerDoc->m_fSendDataOnStop  = TRUE;

         ServerDoc_SendAdvise(
               lpServerDoc,
               OLE_ONDATACHANGE,
               NULL,   /* lpmkDoc -- not relevant here */
               0       /* advf -- no flags necessary */
         );
      }
   }
#endif  // OLE_SERVER
}


/* OutlineDoc_SetRedraw
 * --------------------
 *
 *  Enable/Disable the redraw of the document on screen.
 *  The calls to SetRedraw counted so that nested calls can be handled
 *  properly. calls to SetRedraw must be balanced.
 *
 *  fEnbaleDraw = TRUE      - enable redraw
 *                FALSE     - disable redraw
 */
void OutlineDoc_SetRedraw(LPOUTLINEDOC lpOutlineDoc, BOOL fEnableDraw)
{
   static HCURSOR hPrevCursor = NULL;

   if (fEnableDraw) {
      if (lpOutlineDoc->m_nDisableDraw == 0)
         return;     // already enabled; no state transition

      if (--lpOutlineDoc->m_nDisableDraw > 0)
         return;     // drawing should still be disabled
   } else {
      if (lpOutlineDoc->m_nDisableDraw++ > 0)
         return;     // already disabled; no state transition
   }

   if (lpOutlineDoc->m_nDisableDraw > 0) {
      // this may take a while, put up hourglass cursor
      hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
   } else {
      if (hPrevCursor) {
         SetCursor(hPrevCursor);     // restore original cursor
         hPrevCursor = NULL;
      }
   }

#if defined( OLE_SERVER )
   /* OLE2NOTE: for the Server version, while Redraw is disabled
   **    postpone sending advise notifications until Redraw is re-enabled.
   */
   {
      LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;
      LPSERVERNAMETABLE lpServerNameTable =
            (LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable;

      if (lpOutlineDoc->m_nDisableDraw == 0) {
         /* drawing is being Enabled. if changes occurred while drawing
         **    was disabled, then notify clients now.
         */
         if (lpServerDoc->m_fDataChanged)
            ServerDoc_SendAdvise(
                  lpServerDoc,
                  OLE_ONDATACHANGE,
                  NULL,   /* lpmkDoc -- not relevant here */
                  0       /* advf -- no flags necessary */
            );

         /* OLE2NOTE: send pending change notifications for pseudo objs. */
         ServerNameTable_SendPendingAdvises(lpServerNameTable);

      }
   }
#endif      // OLE_SERVER

#if defined( OLE_CNTR )
   /* OLE2NOTE: for the Container version, while Redraw is disabled
   **    postpone updating the extents of OLE objects until Redraw is
   **    re-enabled.
   */
   {
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;

      /* Update the extents of any OLE object that is marked that
      **    its size may  have changed. when an
      **    IAdviseSink::OnViewChange notification is received,
      **    the corresponding ContainerLine is marked
      **    (m_fDoGetExtent==TRUE) and a message
      **    (WM_U_UPDATEOBJECTEXTENT) is posted to the document
      **    indicating that there are dirty objects.
      */
      if (lpOutlineDoc->m_nDisableDraw == 0)
         ContainerDoc_UpdateExtentOfAllOleObjects(lpContainerDoc);
   }
#endif      // OLE_CNTR

   // enable/disable redraw of the LineList listbox
   LineList_SetRedraw(&lpOutlineDoc->m_LineList, fEnableDraw);
}


/* OutlineDoc_SetSel
 * -----------------
 *
 *      Set the selection in the documents's LineList
 */
void OutlineDoc_SetSel(LPOUTLINEDOC lpOutlineDoc, LPLINERANGE lplrSel)
{
   LineList_SetSel(&lpOutlineDoc->m_LineList, lplrSel);
}


/* OutlineDoc_GetSel
 * -----------------
 *
 *      Get the selection in the documents's LineList.
 *
 *      Returns the count of items selected
 */
int OutlineDoc_GetSel(LPOUTLINEDOC lpOutlineDoc, LPLINERANGE lplrSel)
{
   return LineList_GetSel(&lpOutlineDoc->m_LineList, lplrSel);
}


/* OutlineDoc_ForceRedraw
 * ----------------------
 *
 *      Force the document window to repaint.
 */
void OutlineDoc_ForceRedraw(LPOUTLINEDOC lpOutlineDoc, BOOL fErase)
{
   if (!lpOutlineDoc)
      return;

   LineList_ForceRedraw(&lpOutlineDoc->m_LineList, fErase);
   Heading_CH_ForceRedraw(&lpOutlineDoc->m_heading, fErase);
   Heading_RH_ForceRedraw(&lpOutlineDoc->m_heading, fErase);
}


/* OutlineDoc_RenderFormat
 * -----------------------
 *
 *      Render a clipboard format supported by ClipboardDoc
 */
void OutlineDoc_RenderFormat(LPOUTLINEDOC lpOutlineDoc, UINT uFormat)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   HGLOBAL      hData = NULL;

   if (uFormat == lpOutlineApp->m_cfOutline)
      hData = OutlineDoc_GetOutlineData(lpOutlineDoc, NULL);

   else if (uFormat == CF_TEXT)
      hData = OutlineDoc_GetTextData(lpOutlineDoc, NULL);

   else {
      OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgFormatNotSupported);
      return;
   }

   SetClipboardData(uFormat, hData);
}


/* OutlineDoc_RenderAllFormats
 * ---------------------------
 *
 *      Render all formats supported by ClipboardDoc
 */
void OutlineDoc_RenderAllFormats(LPOUTLINEDOC lpOutlineDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   HGLOBAL      hData = NULL;

   OpenClipboard(lpOutlineDoc->m_hWndDoc);

   hData = OutlineDoc_GetOutlineData(lpOutlineDoc, NULL);
   SetClipboardData(lpOutlineApp->m_cfOutline, hData);

   hData = OutlineDoc_GetTextData(lpOutlineDoc, NULL);
   SetClipboardData(CF_TEXT, hData);

   CloseClipboard();
}



/* OutlineDoc_GetOutlineData
 * -------------------------
 *
 * Return a handle to an array of TextLine objects for the desired line
 *      range.
 *  NOTE: if lplrSel == NULL, then all lines are returned
 *
 */
HGLOBAL OutlineDoc_GetOutlineData(LPOUTLINEDOC lpOutlineDoc, LPLINERANGE lplrSel)
{
   HGLOBAL     hOutline  = NULL;
   LPLINELIST  lpLL=(LPLINELIST)&lpOutlineDoc->m_LineList;
   LPLINE      lpLine;
   LPTEXTLINE  arrLine;
   int     i;
   int     nStart = (lplrSel ? lplrSel->m_nStartLine : 0);
   int     nEnd =(lplrSel ? lplrSel->m_nEndLine : LineList_GetCount(lpLL)-1);
   int     nLines = nEnd - nStart + 1;
   int     nCopied = 0;

   hOutline=GlobalAlloc(GALLOCFLG, sizeof(TEXTLINE)*nLines);

   if (! hOutline) return NULL;

   arrLine=(LPTEXTLINE)GlobalLock(hOutline);

   for (i = nStart; i <= nEnd; i++) {
      lpLine=LineList_GetLine(lpLL, i);
      if (lpLine && Line_GetOutlineData(lpLine, &arrLine[nCopied]))
         nCopied++;
   }

   GlobalUnlock(hOutline);

   return hOutline;
}



/* OutlineDoc_GetTextData
 * ----------------------
 *
 * Return a handle to an object's data in text form for the desired line
 *      range.
 *  NOTE: if lplrSel == NULL, then all lines are returned
 *
 */
HGLOBAL OutlineDoc_GetTextData(LPOUTLINEDOC lpOutlineDoc, LPLINERANGE lplrSel)
{
   LPLINELIST  lpLL=(LPLINELIST)&lpOutlineDoc->m_LineList;
   LPLINE  lpLine;
   HGLOBAL hText = NULL;
   LPOLESTR   lpszText = NULL;
   DWORD   dwMemSize=0;
   int     i,j;
   int     nStart = (lplrSel ? lplrSel->m_nStartLine : 0);
   int     nEnd =(lplrSel ? lplrSel->m_nEndLine : LineList_GetCount(lpLL)-1);
   int     nTabLevel;

   // calculate memory size required
   for(i = nStart; i <= nEnd; i++) {
      lpLine=LineList_GetLine(lpLL, i);
      if (! lpLine)
         continue;

      dwMemSize += Line_GetTabLevel(lpLine);
      dwMemSize += Line_GetTextLen(lpLine);

      dwMemSize += 2; // add 1 for '\r\n' at the end of each line
   }
   dwMemSize++;        // add 1 for '\0' at the end of string

   if(!(hText = GlobalAlloc(GALLOCFLG, dwMemSize)))
      return NULL;

   if(!(lpszText = (LPOLESTR)GlobalLock(hText)))
      return NULL;

   // put line text to memory
   for(i = nStart; i <= nEnd; i++) {
      lpLine=LineList_GetLine(lpLL, i);
      if (! lpLine)
         continue;

      nTabLevel=Line_GetTabLevel(lpLine);
      for(j = 0; j < nTabLevel; j++)
         *lpszText++='\t';

      Line_GetTextData(lpLine, lpszText);
      while(*lpszText)
         lpszText++;     // advance to end of string

      *lpszText++ = '\r';
      *lpszText++ = '\n';
   }

   GlobalUnlock (hText);

   return hText;
}


/* OutlineDoc_SaveToFile
 * ---------------------
 *
 *      Save the document to a file with the same name as stored in the
 * document
 */
BOOL OutlineDoc_SaveToFile(LPOUTLINEDOC lpOutlineDoc, LPCOLESTR lpszFileName, UINT uFormat, BOOL fRemember)
{
#if defined( OLE_CNTR )
   // Call OLE container specific function instead
   return ContainerDoc_SaveToFile(
         (LPCONTAINERDOC)lpOutlineDoc,
         lpszFileName,
         uFormat,
         fRemember
   );

#else

   LPSTORAGE lpDestStg = NULL;
   HRESULT hrErr;
   BOOL fStatus;
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

   if (fRemember) {
      if (lpszFileName) {
         fStatus = OutlineDoc_SetFileName(
               lpOutlineDoc,
               (LPOLESTR)lpszFileName,
               NULL
         );
         if (! fStatus) goto error;
      } else
         lpszFileName = lpOutlineDoc->m_szFileName; // use cur. file name
   } else if (! lpszFileName) {
      goto error;
   }

   hrErr = StgCreateDocfile(
         lpszFileName,
         STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
         0,
         &lpDestStg
   );

   OleDbgAssertSz(hrErr == NOERROR, "Could not create Docfile");
   if (hrErr != NOERROR)
      goto error;

#if defined( OLE_SERVER )

   /*  OLE2NOTE: we must be sure to write our class ID into our
   **    storage. this information is used by OLE to determine the
   **    class of the data stored in our storage. Even for top
   **    "file-level" objects this information should be written to
   **    the file.
   */
   if(WriteClassStg(lpDestStg, &CLSID_APP) != NOERROR)
      goto error;
#endif

   fStatus = OutlineDoc_SaveSelToStg(
         lpOutlineDoc,
         NULL,
         uFormat,
         lpDestStg,
         FALSE,      /* fSameAsLoad */
         fRemember
   );
   if (! fStatus) goto error;

   OleStdRelease((LPUNKNOWN)lpDestStg);

   if (fRemember)
      OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE);

#if defined( OLE_SERVER )

   /* OLE2NOTE: (SERVER-ONLY) inform any linking clients that the
   **    document has been saved. in addition, any currently active
   **    pseudo objects should also inform their clients.
   */
   ServerDoc_SendAdvise (
         (LPSERVERDOC)lpOutlineDoc,
         OLE_ONSAVE,
         NULL,   /* lpmkDoc -- not relevant here */
         0       /* advf -- not relevant here */
   );

#endif

   return TRUE;

error:
   if (lpDestStg)
      OleStdRelease((LPUNKNOWN)lpDestStg);

   OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgSaving);
   return FALSE;

#endif  // ! OLE_CNTR
}


/* OutlineDoc_LoadFromFile
 * -----------------------
 *
 *      Load a document from a file
 */
BOOL OutlineDoc_LoadFromFile(LPOUTLINEDOC lpOutlineDoc, LPOLESTR lpszFileName)
{
   LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPLINELIST      lpLL = &lpOutlineDoc->m_LineList;
   HRESULT         hrErr;
   SCODE           sc;
   LPSTORAGE       lpSrcStg;
   BOOL            fStatus;

   hrErr = StgOpenStorage(lpszFileName,
         NULL,
#if defined( OLE_CNTR )
         STGM_READWRITE | STGM_TRANSACTED | STGM_SHARE_DENY_WRITE,
#else
         STGM_READ | STGM_SHARE_DENY_WRITE,
#endif
         NULL,
         0,
         &lpSrcStg
   );

   if ((sc = GetScode(hrErr)) == STG_E_FILENOTFOUND) {
      OutlineApp_ErrorMessage(lpOutlineApp, OLESTR("File not found"));
      return FALSE;
   } else if (sc == STG_E_FILEALREADYEXISTS) {
      OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgFormat);
      return FALSE;
   } else if (sc != S_OK) {
      OleDbgOutScode("StgOpenStorage returned", sc);
      OutlineApp_ErrorMessage(
            lpOutlineApp,
            OLESTR("File already in use--could not be opened")
      );
      return FALSE;
   }

   if(! OutlineDoc_LoadFromStg(lpOutlineDoc, lpSrcStg)) goto error;

   fStatus = OutlineDoc_SetFileName(lpOutlineDoc, lpszFileName, lpSrcStg);
   if (! fStatus) goto error;

   OleStdRelease((LPUNKNOWN)lpSrcStg);

   return TRUE;

error:
   OleStdRelease((LPUNKNOWN)lpSrcStg);
   OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgOpening);
   return FALSE;
}



/* OutlineDoc_LoadFromStg
 * ----------------------
 *
 *  Load entire document from an open IStorage pointer (lpSrcStg)
 *      Return TRUE if ok, FALSE if error.
 */
BOOL OutlineDoc_LoadFromStg(LPOUTLINEDOC lpOutlineDoc, LPSTORAGE lpSrcStg)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   HRESULT hrErr;
   BOOL fStatus;
   ULONG nRead;
   LINERANGE lrSel = { 0, 0 };
   LPSTREAM lpLLStm;
   OUTLINEDOCHEADER docRecord;
   OUTLINEDOCHEADER_ONDISK docRecordOnDisk;

   hrErr = lpSrcStg->lpVtbl->OpenStream(
         lpSrcStg,
         OLESTR("LineList"),
         NULL,
         STGM_READ | STGM_SHARE_EXCLUSIVE,
         0,
         &lpLLStm
   );

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

   /* read OutlineDoc header record */
   hrErr = lpLLStm->lpVtbl->Read(
         lpLLStm,
         (LPVOID)&docRecordOnDisk,
         sizeof(docRecordOnDisk),
         &nRead
   );

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

    //  Transform docRecordOnDisk into docRecord
    //  Compilers should handle aligment correctly
    OLESTRCPY(docRecord.m_szFormatName, docRecordOnDisk.m_szFormatName);
    docRecord.m_narrAppVersionNo[0] = (int) docRecordOnDisk.m_narrAppVersionNo[0];
    docRecord.m_narrAppVersionNo[1] = (int) docRecordOnDisk.m_narrAppVersionNo[1];
    docRecord.m_fShowHeading = (BOOL) docRecordOnDisk.m_fShowHeading;
    docRecord.m_reserved1 = docRecordOnDisk.m_reserved1;
    docRecord.m_reserved2 = docRecordOnDisk.m_reserved2;
    docRecord.m_reserved3 = docRecordOnDisk.m_reserved3;
    docRecord.m_reserved4 = docRecordOnDisk.m_reserved4;

   fStatus = OutlineApp_VersionNoCheck(
         lpOutlineApp,
         docRecord.m_szFormatName,
         docRecord.m_narrAppVersionNo
   );

   /* storage is an incompatible version; file can not be read */
   if (! fStatus)
      goto error;

   lpOutlineDoc->m_heading.m_fShow = docRecord.m_fShowHeading;

#if defined( OLE_SERVER )
   {
      // Load ServerDoc specific data
      LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;
#if defined( SVR_TREATAS )
      LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
      CLSID       clsid;
      CLIPFORMAT  cfFmt;
      LPOLESTR       lpszType;
#endif  // SVR_TREATAS

      lpServerDoc->m_nNextRangeNo = (ULONG)docRecord.m_reserved1;

#if defined( SVR_TREATAS )
      /* OLE2NOTE: if the Server is capable of supporting "TreatAs"
      **    (aka. ActivateAs), it must read the class that is written
      **    into the storage. if this class is NOT the app's own
      **    class ID, then this is a TreatAs operation. the server
      **    then must faithfully pretend to be the class that is
      **    written into the storage. it must also faithfully write
      **    the data back to the storage in the SAME format as is
      **    written in the storage.
      **
      **    SVROUTL and ISVROTL can emulate each other. they have the
      **    simplification that they both read/write the identical
      **    format. thus for these apps no actual conversion of the
      **    native bits is actually required.
      */
      lpServerDoc->m_clsidTreatAs = CLSID_NULL;
      if (OleStdGetTreatAsFmtUserType(&CLSID_APP, lpSrcStg, &clsid,
                     (CLIPFORMAT FAR*)&cfFmt, (LPOLESTR FAR*)&lpszType)) {

         if (cfFmt == lpOutlineApp->m_cfOutline) {
            // We should perform TreatAs operation
            if (lpServerDoc->m_lpszTreatAsType)
               OleStdFreeString(lpServerDoc->m_lpszTreatAsType, NULL);

            lpServerDoc->m_clsidTreatAs = clsid;
            ((LPOUTLINEDOC)lpServerDoc)->m_cfSaveFormat = cfFmt;
            lpServerDoc->m_lpszTreatAsType = lpszType;

            OleDbgOut3("OutlineDoc_LoadFromStg: TreateAs ==> '");
//          OleDbgOutNoPrefix3(lpServerDoc->m_lpszTreatAsType);
            OleDbgOutNoPrefix3("'\r\n");
         } else {
            // ERROR: we ONLY support TreatAs for CF_OUTLINE format
            OleDbgOut("SvrDoc_PStg_InitNew: INVALID TreatAs Format\r\n");
            OleStdFreeString(lpszType, NULL);
         }
      }
#endif  // SVR_TREATAS
   }
#endif  // OLE_SVR
#if defined( OLE_CNTR )
   {
      // Load ContainerDoc specific data
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;

      lpContainerDoc->m_nNextObjNo = (ULONG)docRecord.m_reserved2;
   }
#endif  // OLE_CNTR

   OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );

   if(! LineList_LoadFromStg(&lpOutlineDoc->m_LineList, lpSrcStg, lpLLStm))
      goto error;
   if(! OutlineNameTable_LoadFromStg(lpOutlineDoc->m_lpNameTable, lpSrcStg))
      goto error;

   OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE);
   OutlineDoc_SetSel(lpOutlineDoc, &lrSel);

   OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

   OleStdRelease((LPUNKNOWN)lpLLStm);

#if defined( OLE_CNTR )
   {
      LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineDoc;

      /* A ContainerDoc keeps its storage open at all times. it is necessary
      *   to AddRef the lpSrcStg in order to hang on to it.
      */
      if (lpOleDoc->m_lpStg) {
         OleStdVerifyRelease((LPUNKNOWN)lpOleDoc->m_lpStg,
               OLESTR("Doc Storage not released properly"));
      }
      lpSrcStg->lpVtbl->AddRef(lpSrcStg);
      lpOleDoc->m_lpStg = lpSrcStg;
   }
#endif      // OLE_CNTR

   return TRUE;

error:
   OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );
   if (lpLLStm)
      OleStdRelease((LPUNKNOWN)lpLLStm);
   return FALSE;
}


/* OutlineDoc_SaveSelToStg
 * -----------------------
 *
 *      Save the specified selection of document into an IStorage*. All lines
 * within the selection along with any names completely contained within the
 * selection will be written
 *
 *      Return TRUE if ok, FALSE if error
 */
BOOL OutlineDoc_SaveSelToStg(
      LPOUTLINEDOC        lpOutlineDoc,
      LPLINERANGE         lplrSel,
      UINT                uFormat,
      LPSTORAGE           lpDestStg,
      BOOL                fSameAsLoad,
      BOOL                fRemember
)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   HRESULT hrErr = NOERROR;
   LPSTREAM lpLLStm = NULL;
   LPSTREAM lpNTStm = NULL;
   ULONG nWritten;
   BOOL fStatus;
   OUTLINEDOCHEADER docRecord;
   OUTLINEDOCHEADER_ONDISK docRecordOnDisk;
   HCURSOR  hPrevCursor;
   char szAnsiString[256];

#if defined( OLE_VERSION )
   LPOLESTR lpszUserType;
   LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineDoc;

   /*  OLE2NOTE: we must be sure to write the information required for
   **    OLE into our docfile. this includes user type
   **    name, data format, etc. Even for top "file-level" objects
   **    this information should be written to the file. Both
   **    containters and servers should write this information.
   */

#if defined( OLE_SERVER ) && defined( SVR_TREATAS )
   LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;

   /* OLE2NOTE: if the Server is emulating another class (ie.
   **    "TreatAs" aka. ActivateAs), it must write the same user type
   **    name and format that was was originally written into the
   **    storage rather than its own user type name.
   **
   **    SVROUTL and ISVROTL can emulate each other. they have the
   **    simplification that they both read/write the identical
   **    format. thus for these apps no actual conversion of the
   **    native bits is actually required.
   */
   if (! IsEqualCLSID(&lpServerDoc->m_clsidTreatAs, &CLSID_NULL))
      lpszUserType = lpServerDoc->m_lpszTreatAsType;
   else
#endif  // OLE_SERVER && SVR_TREATAS

      lpszUserType = (LPOLESTR)FULLUSERTYPENAME;

   hrErr = WriteFmtUserTypeStg(lpDestStg, (CLIPFORMAT)uFormat, lpszUserType);
   if(hrErr != NOERROR) goto error;

   if (fSameAsLoad) {
      /* OLE2NOTE: we are saving into to same storage that we were
      **    passed an load time. we deliberatly opened the streams we
      **    need (lpLLStm and lpNTStm) at load time, so that we can
      **    robustly save at save time in a low-memory situation.
      **    this is particulary important the embedded objects do NOT
      **    consume additional memory when
      **    IPersistStorage::Save(fSameAsLoad==TRUE) is called.
      */
      LARGE_INTEGER libZero;
      ULARGE_INTEGER ulibZero;
      LISet32( libZero, 0 );
      LISet32( ulibZero, 0 );
      lpLLStm = lpOleDoc->m_lpLLStm;

      /*  because this is the fSameAsLoad==TRUE case, we will save
      **    into the streams that we hold open. we will AddRef the
      **    stream here so that the release below will NOT close the
      **    stream.
      */
      lpLLStm->lpVtbl->AddRef(lpLLStm);

      // truncate the current stream and seek to beginning
      lpLLStm->lpVtbl->SetSize(lpLLStm, ulibZero);
      lpLLStm->lpVtbl->Seek(
            lpLLStm, libZero, STREAM_SEEK_SET, NULL);

      lpNTStm = lpOleDoc->m_lpNTStm;
      lpNTStm->lpVtbl->AddRef(lpNTStm);   // (see comment above)

      // truncate the current stream and seek to beginning
      lpNTStm->lpVtbl->SetSize(lpNTStm, ulibZero);
      lpNTStm->lpVtbl->Seek(
            lpNTStm, libZero, STREAM_SEEK_SET, NULL);
   } else
#endif  // OLE_VERSION
   {
      hrErr = lpDestStg->lpVtbl->CreateStream(
            lpDestStg,
            OLESTR("LineList"),
            STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
            0,
            0,
            &lpLLStm
      );
      if (hrErr != NOERROR) {
         OleDbgAssertSz(hrErr==NOERROR,"Could not create LineList stream");
         OleDbgOutHResult("LineList CreateStream returned", hrErr);
         goto error;
      }

      hrErr = lpDestStg->lpVtbl->CreateStream(
            lpDestStg,
            OLESTR("NameTable"),
            STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
            0,
            0,
            &lpNTStm
      );
      if (hrErr != NOERROR) {
         OleDbgAssertSz(hrErr==NOERROR,"Could not create NameTable stream");
         OleDbgOutHResult("NameTable CreateStream returned", hrErr);
         goto error;
      }
   }

   // this may take a while, put up hourglass cursor
   hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

   _fmemset((LPOUTLINEDOCHEADER)&docRecord,0,sizeof(OUTLINEDOCHEADER));
   GetClipboardFormatName(
         uFormat,
         szAnsiString,
         256
   );
   A2W(szAnsiString, docRecord.m_szFormatName, lstrlen(szAnsiString)+1);
   OutlineApp_GetAppVersionNo(lpOutlineApp, docRecord.m_narrAppVersionNo);

   docRecord.m_fShowHeading = lpOutlineDoc->m_heading.m_fShow;

#if defined( OLE_SERVER )
   {
      // Store ServerDoc specific data
      LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;

      docRecord.m_reserved1 = (DWORD)lpServerDoc->m_nNextRangeNo;
   }
#endif
#if defined( OLE_CNTR )
   {
      // Store ContainerDoc specific data
      LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOutlineDoc;

      docRecord.m_reserved2 = (DWORD)lpContainerDoc->m_nNextObjNo;
   }
#endif

   /* write OutlineDoc header record */

    //  Transform docRecord into docRecordOnDisk
    //  Compilers should handle aligment correctly
    OLESTRCPY(docRecordOnDisk.m_szFormatName, docRecord.m_szFormatName);
    docRecordOnDisk.m_narrAppVersionNo[0] = (short) docRecord.m_narrAppVersionNo[0];
    docRecordOnDisk.m_narrAppVersionNo[1] = (short) docRecord.m_narrAppVersionNo[1];
    docRecordOnDisk.m_fShowHeading = (USHORT) docRecord.m_fShowHeading;
    docRecordOnDisk.m_reserved1 = docRecord.m_reserved1;
    docRecordOnDisk.m_reserved2 = docRecord.m_reserved2;
    docRecordOnDisk.m_reserved3 = docRecord.m_reserved3;
    docRecordOnDisk.m_reserved4 = docRecord.m_reserved4;
   hrErr = lpLLStm->lpVtbl->Write(
         lpLLStm,
         (LPVOID)&docRecordOnDisk,
         sizeof(docRecordOnDisk),
         &nWritten
      );

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

   // Save LineList
   /* OLE2NOTE: A ContainerDoc keeps its storage open at all times. It is
   **    necessary to pass the current open storage (lpOleDoc->m_lpStg)
   **    to the LineList_SaveSelToStg method so that currently written data
   **    for any embeddings is also saved to the new destination
   **    storage. The data required by a contained object is both the
   **    ContainerLine information and the associated sub-storage that is
   **    written directly by the embedded object.
   */
   fStatus = LineList_SaveSelToStg(
      &lpOutlineDoc->m_LineList,
         lplrSel,
         uFormat,
#if defined( OLE_CNTR )
         lpOleDoc->m_lpStg,
#else
         NULL,
#endif
         lpDestStg,
         lpLLStm,
         fRemember
   );
   if (! fStatus) goto error;

   // Save associated NameTable
   fStatus = OutlineNameTable_SaveSelToStg(
         lpOutlineDoc->m_lpNameTable,
         lplrSel,
         uFormat,
         lpNTStm
   );

   if (! fStatus) goto error;

   OleStdRelease((LPUNKNOWN)lpLLStm);
   lpOutlineDoc->m_cfSaveFormat = uFormat;  // remember format used to save

   SetCursor(hPrevCursor);     // restore original cursor
   return TRUE;

error:
   if (lpLLStm)
      OleStdRelease((LPUNKNOWN)lpLLStm);

   SetCursor(hPrevCursor);     // restore original cursor
   return FALSE;
}


/* OutlineDoc_Print
 * ----------------
 *  Prints the contents of the list box in HIMETRIC mapping mode. Origin
 *  remains to be the upper left corner and the print proceeds down the
 *  page using a negative y-cordinate.
 *
 */
void OutlineDoc_Print(LPOUTLINEDOC lpOutlineDoc, HDC hDC)
{
   LPLINELIST lpLL = &lpOutlineDoc->m_LineList;
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   WORD    nIndex;
   WORD    nTotal;
   int     dy;
   BOOL    fError = FALSE;
   LPLINE  lpLine;
   RECT    rcLine;
   RECT    rcPix;
   RECT    rcHim;
   RECT    rcWindowOld;
   RECT    rcViewportOld;
   HFONT   hOldFont;
   DOCINFO di;         /* Document information for StartDoc function */
   char    szAnsiStr[256];

   /* Get dimension of page */
   rcPix.left = 0;
   rcPix.top = 0;
   rcPix.right = GetDeviceCaps(hDC, HORZRES);
   rcPix.bottom = GetDeviceCaps(hDC, VERTRES);

   SetDCToDrawInHimetricRect(hDC, (LPRECT)&rcPix, (LPRECT)&rcHim,
         (LPRECT)&rcWindowOld, (LPRECT)&rcViewportOld);

   // Set the default font size, and font face name
   hOldFont = SelectObject(hDC, lpOutlineApp->m_hStdFont);

   /* Get the lines in document */
   nIndex     = 0;
   nTotal  = LineList_GetCount(lpLL);

   /* Create the Cancel dialog */
   // REVIEW: should load dialog title from string resource file
   hWndPDlg = CreateDialog (
         lpOutlineApp->m_hInst,
         "Print",
         lpOutlineApp->m_hWndApp,
         (DLGPROC)PrintDlgProc
   );

   if(!hWndPDlg)
      goto getout;

   /* Allow the app. to inform GDI of the abort function to call */
   if(SetAbortProc(hDC, (ABORTPROC)AbortProc) < 0) {
      fError = TRUE;
      goto getout3;
   }

   /* Disable the main application window */
   EnableWindow (lpOutlineApp->m_hWndApp, FALSE);

   // initialize the rectangle for the first line
   rcLine.left = rcHim.left;
   rcLine.bottom = rcHim.top;

   /* Initialize the document */
   fCancelPrint = FALSE;

   di.cbSize = sizeof(DOCINFO);
   W2A (lpOutlineDoc->m_lpszDocTitle, szAnsiStr, 256);
   di.lpszDocName = szAnsiStr;
   di.lpszOutput = NULL;

   if(StartDoc(hDC, (DOCINFO FAR*)&di) <= 0) {
      fError = TRUE;
      OleDbgOut2("StartDoc error\n");
      goto getout5;
   }

   if(StartPage(hDC) <= 0) {       // start first page
      fError = TRUE;
      OleDbgOut2("StartPage error\n");
      goto getout2;
   }

   /* While more lines print out the text */
   while(nIndex < nTotal) {
      lpLine = LineList_GetLine(lpLL, nIndex);
      if (! lpLine)
         continue;

      dy = Line_GetHeightInHimetric(lpLine);

      /* Reached end of page. Tell the device driver to eject a page */
      if(rcLine.bottom - dy < rcHim.bottom) {
         if (EndPage(hDC) < 0) {
            fError=TRUE;
            OleDbgOut2("EndPage error\n");
            goto getout2;
         }

         // NOTE: Reset the Mapping mode of DC
         SetDCToDrawInHimetricRect(hDC, (LPRECT)&rcPix, (LPRECT)&rcHim,
               (LPRECT)&rcWindowOld, (LPRECT)&rcViewportOld);

         // Set the default font size, and font face name
         SelectObject(hDC, lpOutlineApp->m_hStdFont);

         if (StartPage(hDC) <= 0) {
            fError=TRUE;
            OleDbgOut2("StartPage error\n");
            goto getout2;
         }

         rcLine.bottom = rcHim.top;
      }

      rcLine.top = rcLine.bottom;
      rcLine.bottom -= dy;
      rcLine.right = rcLine.left + Line_GetWidthInHimetric(lpLine);

      /* Print the line */
      Line_Draw(lpLine, hDC, &rcLine, NULL, FALSE /*fHighlight*/);

      OleDbgOut2("a line is drawn\n");

      /* Test and see if the Abort flag has been set. If yes, exit. */
      if (fCancelPrint)
         goto getout2;

      /* Move down the page */
      nIndex++;
   }

   {
      int nCode;

      /* Eject the last page. */
      if((nCode = EndPage(hDC)) < 0) {
#if defined( _DEBUG )
         char szBuf[255];
         wsprintf(szBuf, "EndPage error code is %d\n", nCode);
         OleDbgOut2(szBuf);
#endif
         fError=TRUE;
         goto getout2;
      }
   }


   /* Complete the document. */
   if(EndDoc(hDC) < 0) {
      fError=TRUE;
      OleDbgOut2("EndDoc error\n");

getout2:
      /* Ran into a problem before NEWFRAME? Abort the document */
      AbortDoc(hDC);
   }

getout5:
   /* Re-enable main app. window */
   EnableWindow (lpOutlineApp->m_hWndApp, TRUE);

getout3:
   /* Close the cancel dialog */
   DestroyWindow (hWndPDlg);

getout:

   /* Error? make sure the user knows... */
   if(fError || CommDlgExtendedError())
      OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgPrint);

   SelectObject(hDC, hOldFont);
}





/* OutlineDoc_DialogHelp
 * ---------------------
 *
 *  Show help message for ole2ui dialogs.
 *
 * Parameters:
 *
 *   hDlg      HWND to the dialog the help message came from - use
 *             this in the call to WinHelp/MessageBox so that
 *             activation/focus goes back to the dialog, and not the
 *             main window.
 *
 *   wParam    ID of the dialog (so we know what type of dialog it is).
 */
void OutlineDoc_DialogHelp(HWND hDlg, WPARAM wDlgID)
{

   char szMessageBoxText[64];

   if (!IsWindow(hDlg))  // don't do anything if we've got a bogus hDlg.
    return;

   lstrcpy(szMessageBoxText, "Help Message for ");

   switch (wDlgID)
   {

   case IDD_CONVERT:
      lstrcat(szMessageBoxText, "Convert");
      break;

   case IDD_CHANGEICON:
      lstrcat(szMessageBoxText, "Change Icon");
      break;

   case IDD_INSERTOBJECT:
      lstrcat(szMessageBoxText, "Insert Object");
      break;

   case IDD_PASTESPECIAL:
      lstrcat(szMessageBoxText, "Paste Special");
      break;

   case IDD_EDITLINKS:
      lstrcat(szMessageBoxText, "Edit Links");
      break;

   case IDD_CHANGESOURCE:
      lstrcat(szMessageBoxText, "Change Source");
      break;

   case IDD_INSERTFILEBROWSE:
      lstrcat(szMessageBoxText, "Insert From File Browse");
      break;

   case IDD_CHANGEICONBROWSE:
      lstrcat(szMessageBoxText, "Change Icon Browse");
      break;

   default:
      lstrcat(szMessageBoxText, "Unknown");
      break;
   }

   lstrcat(szMessageBoxText, " Dialog.");

   // You'd probably really a call to WinHelp here.
   MessageBox(hDlg, szMessageBoxText, "Help", MB_OK);

   return;
}


/* OutlineDoc_SetCurrentZoomCommand
 * --------------------------------
 *
 *  Set current zoom level to be checked in the menu.
 *  Set the corresponding scalefactor for the document.
 */
void OutlineDoc_SetCurrentZoomCommand(
      LPOUTLINEDOC        lpOutlineDoc,
      UINT                uCurrentZoom
)
{
   SCALEFACTOR scale;

   if (!lpOutlineDoc)
      return;

   lpOutlineDoc->m_uCurrentZoom = uCurrentZoom;

   switch (uCurrentZoom) {

#if !defined( OLE_CNTR )
         case IDM_V_ZOOM_400:
            scale.dwSxN = (DWORD) 4;
            scale.dwSxD = (DWORD) 1;
            scale.dwSyN = (DWORD) 4;
            scale.dwSyD = (DWORD) 1;
            break;

         case IDM_V_ZOOM_300:
            scale.dwSxN = (DWORD) 3;
            scale.dwSxD = (DWORD) 1;
            scale.dwSyN = (DWORD) 3;
            scale.dwSyD = (DWORD) 1;
            break;

         case IDM_V_ZOOM_200:
            scale.dwSxN = (DWORD) 2;
            scale.dwSxD = (DWORD) 1;
            scale.dwSyN = (DWORD) 2;
            scale.dwSyD = (DWORD) 1;
            break;
#endif      // !OLE_CNTR

         case IDM_V_ZOOM_100:
            scale.dwSxN = (DWORD) 1;
            scale.dwSxD = (DWORD) 1;
            scale.dwSyN = (DWORD) 1;
            scale.dwSyD = (DWORD) 1;
            break;

         case IDM_V_ZOOM_75:
            scale.dwSxN = (DWORD) 3;
            scale.dwSxD = (DWORD) 4;
            scale.dwSyN = (DWORD) 3;
            scale.dwSyD = (DWORD) 4;
            break;

         case IDM_V_ZOOM_50:
            scale.dwSxN = (DWORD) 1;
            scale.dwSxD = (DWORD) 2;
            scale.dwSyN = (DWORD) 1;
            scale.dwSyD = (DWORD) 2;
            break;

         case IDM_V_ZOOM_25:
            scale.dwSxN = (DWORD) 1;
            scale.dwSxD = (DWORD) 4;
            scale.dwSyN = (DWORD) 1;
            scale.dwSyD = (DWORD) 4;
            break;
   }

   OutlineDoc_SetScaleFactor(lpOutlineDoc, (LPSCALEFACTOR)&scale, NULL);
}


/* OutlineDoc_GetCurrentZoomMenuCheck
 * ----------------------------------
 *
 *  Get current zoom level to be checked in the menu.
 */
UINT OutlineDoc_GetCurrentZoomMenuCheck(LPOUTLINEDOC lpOutlineDoc)
{
   return lpOutlineDoc->m_uCurrentZoom;
}


/* OutlineDoc_SetScaleFactor
 * -------------------------
 *
 *  Set the scale factor of the document which will affect the
 *      size of the document on the screen
 *
 * Parameters:
 *
 *   scale      structure containing x and y scales
 */
void OutlineDoc_SetScaleFactor(
      LPOUTLINEDOC        lpOutlineDoc,
      LPSCALEFACTOR       lpscale,
      LPRECT              lprcDoc
)
{
   LPLINELIST      lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
   HWND            hWndLL = LineList_GetWindow(lpLL);

   if (!lpOutlineDoc || !lpscale)
      return;

   InvalidateRect(hWndLL, NULL, TRUE);

   lpOutlineDoc->m_scale = *lpscale;
   LineList_ReScale((LPLINELIST)&lpOutlineDoc->m_LineList, lpscale);

#if defined( USE_HEADING )
   Heading_ReScale((LPHEADING)&lpOutlineDoc->m_heading, lpscale);
#endif

   OutlineDoc_Resize(lpOutlineDoc, lprcDoc);
}


/* OutlineDoc_GetScaleFactor
 * -------------------------
 *
 *  Retrieve the scale factor of the document
 *
 * Parameters:
 *
 */
LPSCALEFACTOR OutlineDoc_GetScaleFactor(LPOUTLINEDOC lpOutlineDoc)
{
   if (!lpOutlineDoc)
      return NULL;

   return (LPSCALEFACTOR)&lpOutlineDoc->m_scale;
}


/* OutlineDoc_SetCurrentMarginCommand
 * ----------------------------------
 *
 *  Set current Margin level to be checked in the menu.
 */
void OutlineDoc_SetCurrentMarginCommand(
      LPOUTLINEDOC        lpOutlineDoc,
      UINT                uCurrentMargin
)
{
   if (!lpOutlineDoc)
      return;

   lpOutlineDoc->m_uCurrentMargin = uCurrentMargin;

   switch (uCurrentMargin) {
      case IDM_V_SETMARGIN_0:
         OutlineDoc_SetMargin(lpOutlineDoc, 0, 0);
         break;

      case IDM_V_SETMARGIN_1:
         OutlineDoc_SetMargin(lpOutlineDoc, 1000, 1000);
         break;

      case IDM_V_SETMARGIN_2:
         OutlineDoc_SetMargin(lpOutlineDoc, 2000, 2000);
         break;

      case IDM_V_SETMARGIN_3:
         OutlineDoc_SetMargin(lpOutlineDoc, 3000, 3000);
         break;

      case IDM_V_SETMARGIN_4:
         OutlineDoc_SetMargin(lpOutlineDoc, 4000, 4000);
         break;
   }
}


/* OutlineDoc_GetCurrentMarginMenuCheck
 * ------------------------------------
 *
 *  Get current Margin level to be checked in the menu.
 */
UINT OutlineDoc_GetCurrentMarginMenuCheck(LPOUTLINEDOC lpOutlineDoc)
{
   return lpOutlineDoc->m_uCurrentMargin;
}


/* OutlineDoc_SetMargin
 * --------------------
 *
 *  Set the left and right margin of the document
 *
 * Parameters:
 *      nLeftMargin  - left margin in Himetric values
 *      nRightMargin - right margin in Himetric values
 */
void OutlineDoc_SetMargin(LPOUTLINEDOC lpOutlineDoc, int nLeftMargin, int nRightMargin)
{
   LPLINELIST lpLL;
   int        nMaxWidthInHim;

   if (!lpOutlineDoc)
      return;

   lpOutlineDoc->m_nLeftMargin = nLeftMargin;
   lpOutlineDoc->m_nRightMargin = nRightMargin;
   lpLL = OutlineDoc_GetLineList(lpOutlineDoc);

   // Force recalculation of Horizontal extent
   nMaxWidthInHim = LineList_GetMaxLineWidthInHimetric(lpLL);
   LineList_SetMaxLineWidthInHimetric(lpLL, -nMaxWidthInHim);

#if defined( INPLACE_CNTR )
   ContainerDoc_UpdateInPlaceObjectRects((LPCONTAINERDOC)lpOutlineDoc, 0);
#endif

   OutlineDoc_ForceRedraw(lpOutlineDoc, TRUE);
}


/* OutlineDoc_GetMargin
 * --------------------
 *
 *  Get the left and right margin of the document
 *
 *  Parameters:
 *      nLeftMargin  - left margin in Himetric values
 *      nRightMargin - right margin in Himetric values
 *
 *  Returns:
 *      low order word  - left margin
 *      high order word - right margin
 */
LONG OutlineDoc_GetMargin(LPOUTLINEDOC lpOutlineDoc)
{
   if (!lpOutlineDoc)
      return 0;

   return MAKELONG(lpOutlineDoc->m_nLeftMargin, lpOutlineDoc->m_nRightMargin);
}

#if defined( USE_HEADING )

/* OutlineDoc_GetHeading
 * ---------------------
 *
 *      Get Heading Object in OutlineDoc
 */
LPHEADING OutlineDoc_GetHeading(LPOUTLINEDOC lpOutlineDoc)
{
   if (!lpOutlineDoc || lpOutlineDoc->m_fDataTransferDoc)
      return NULL;
   else
      return (LPHEADING)&lpOutlineDoc->m_heading;
}


/* OutlineDoc_ShowHeading
 * ----------------------
 *
 *  Show/Hide document row/column headings.
 */
void OutlineDoc_ShowHeading(LPOUTLINEDOC lpOutlineDoc, BOOL fShow)
{
   LPHEADING   lphead = OutlineDoc_GetHeading(lpOutlineDoc);
#if defined( INPLACE_SVR )
   LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOutlineDoc;
#endif

   if (! lphead)
      return;

   Heading_Show(lphead, fShow);

#if defined( INPLACE_SVR )
   if (lpServerDoc->m_fUIActive) {
      LPINPLACEDATA lpIPData = lpServerDoc->m_lpIPData;

      /* OLE2NOTE: our extents have NOT changed; only our the size of
      **    our object-frame adornments is changing. we can use the
      **    current PosRect and ClipRect and simply resize our
      **    windows WITHOUT informing our in-place container.
      */
      ServerDoc_ResizeInPlaceWindow(
            lpServerDoc,
            (LPRECT)&(lpIPData->rcPosRect),
            (LPRECT)&(lpIPData->rcClipRect)
      );
   } else
#else   // !INPLACE_SVR

   OutlineDoc_Resize(lpOutlineDoc, NULL);

#if defined( INPLACE_CNTR )
   ContainerDoc_UpdateInPlaceObjectRects((LPCONTAINERDOC)lpOutlineDoc, 0);
#endif  // INPLACE_CNTR

#endif  // INPLACE_SVR

   OutlineDoc_ForceRedraw(lpOutlineDoc, TRUE);
}

#endif  // USE_HEADING


/* AbortProc
 * ---------
 *  AborProc is called by GDI print code to check for user abort.
 */
BOOL FAR PASCAL EXPORT AbortProc (HDC hdc, WORD reserved)
{
   MSG msg;

   /* Allow other apps to run, or get abort messages */
   while(! fCancelPrint && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
      if(!hWndPDlg || !IsDialogMessage (hWndPDlg, &msg)) {
         TranslateMessage (&msg);
         DispatchMessage  (&msg);
      }
   }
   return !fCancelPrint;
}


/* PrintDlgProc
 * ------------
 *  Dialog function for the print cancel dialog box.
 *
 *  RETURNS    : TRUE  - OK to abort/ not OK to abort
 *               FALSE - otherwise.
 */
BOOL FAR PASCAL EXPORT PrintDlgProc(
      HWND hwnd,
      WORD msg,
      WORD wParam,
      LONG lParam
)
{
   switch (msg) {
      case WM_COMMAND:
      /* abort printing if the only button gets hit */
         fCancelPrint = TRUE;
         return TRUE;
   }

   return FALSE;
}
