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


#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP g_lpApp;


/* TextLine_Create
 * ---------------
 *
 *      Create a text line object and return the pointer
 */
LPTEXTLINE TextLine_Create(HDC hDC, UINT nTab, LPSTR lpszText)
{
    LPTEXTLINE lpTextLine;

    lpTextLine=(LPTEXTLINE) New((DWORD)sizeof(TEXTLINE));
    if (lpTextLine == NULL) {
        OleDbgAssertSz(lpTextLine!=NULL,"Error allocating TextLine");
        return NULL; 
    }

    TextLine_Init(lpTextLine, nTab, hDC);

	if (lpszText) {
		lpTextLine->m_nLength = lstrlen(lpszText);
		lstrcpy((LPSTR)lpTextLine->m_szText, lpszText);
	} else {
		lpTextLine->m_nLength = 0;
		lpTextLine->m_szText[0] = '\0';
	}

    TextLine_CalcExtents(lpTextLine, hDC);

    return(lpTextLine);
}


/* TextLine_Init
 * -------------
 *
 *      Calculate the width/height of a text line object.
 */
void TextLine_Init(LPTEXTLINE lpTextLine, int nTab, HDC hDC)
{
    Line_Init((LPLINE)lpTextLine, nTab, hDC);   // init the base class fields

    ((LPLINE)lpTextLine)->m_lineType = TEXTLINETYPE;
    lpTextLine->m_nLength = 0;
    lpTextLine->m_szText[0] = '\0';
}


/* TextLine_Delete
 * ---------------
 *
 *      Delete the TextLine structure
 */
void TextLine_Delete(LPTEXTLINE lpTextLine)
{
    Delete((LPVOID)lpTextLine);
}


/* TextLine_Edit
 * -------------
 *
 *      Edit the text line object. 
 *
 *      Returns TRUE if line was changed
 *              FALSE if the line was NOT changed
 */
BOOL TextLine_Edit(LPTEXTLINE lpLine, HWND hWndDoc, HDC hDC)
{
#if defined( USE_FRAMETOOLS )
	LPFRAMETOOLS lptb = OutlineApp_GetFrameTools(g_lpApp);
#endif 
    BOOL fStatus = FALSE;
    
#if defined( USE_FRAMETOOLS )
	FrameTools_FB_GetEditText(lptb, lpLine->m_szText, sizeof(lpLine->m_szText));
#else 
    if (! InputTextDlg(hWndDoc, lpLine->m_szText, "Edit Line"))
		return FALSE;
#endif 

	lpLine->m_nLength = lstrlen(lpLine->m_szText);
	TextLine_CalcExtents(lpLine, hDC);
	fStatus = TRUE;

    return fStatus;
}


/* TextLine_CalcExtents
 * --------------------
 *
 *      Calculate the width/height of a text line object.
 */
void TextLine_CalcExtents(LPTEXTLINE lpTextLine, HDC hDC)
{
    SIZE size;
	LPLINE lpLine = (LPLINE)lpTextLine;

    if (lpTextLine->m_nLength) {
        GetTextExtentPoint(hDC, lpTextLine->m_szText,
                            lpTextLine->m_nLength, &size);
        lpLine->m_nWidthInHimetric=size.cx;
        lpLine->m_nHeightInHimetric=size.cy;
    } else {
        // we still need to calculate proper height even for NULL string
        TEXTMETRIC tm;
        GetTextMetrics(hDC, &tm);

        // required to set height
        lpLine->m_nHeightInHimetric = tm.tmHeight;  
        lpLine->m_nWidthInHimetric = 0;
    }
	
	// Allow size for boundary space
	lpLine->m_nHeightInHimetric += 2 * 
			XformHeightInPixelsToHimetric(hDC, LINE_BOUNDARY_WIDTH);
	lpLine->m_nWidthInHimetric +=  2 *
			XformWidthInPixelsToHimetric(hDC, LINE_BOUNDARY_WIDTH);
	

#if defined( _DEBUG )
    {
        RECT rc;
        rc.left = 0;
        rc.top = 0;
        rc.right = XformWidthInHimetricToPixels(hDC, 
				lpLine->m_nWidthInHimetric);
        rc.bottom = XformHeightInHimetricToPixels(hDC, 
				lpLine->m_nHeightInHimetric);

        OleDbgOutRect3("TextLine_CalcExtents", (LPRECT)&rc);
    }
#endif  
}



/* TextLine_SetHeightInHimetric
 * ----------------------------
 *
 *      Set the height of a textline object.
 */
void TextLine_SetHeightInHimetric(LPTEXTLINE lpTextLine, int nHeight)
{
	if (!lpTextLine)
		return;

	((LPLINE)lpTextLine)->m_nHeightInHimetric = nHeight;
}



/* TextLine_GetTextLen
 * -------------------
 *
 * Return length of string of the TextLine (not considering the tab level).
 */
int TextLine_GetTextLen(LPTEXTLINE lpTextLine)
{
    return lstrlen((LPSTR)lpTextLine->m_szText);
}


/* TextLine_GetTextData
 * --------------------
 *
 * Return the string of the TextLine (not considering the tab level).
 */
void TextLine_GetTextData(LPTEXTLINE lpTextLine, LPSTR lpszBuf)
{
    lstrcpy(lpszBuf, (LPSTR)lpTextLine->m_szText);
}


/* TextLine_GetOutlineData
 * -----------------------
 *
 * Return the CF_OUTLINE format data for the TextLine.
 */
BOOL TextLine_GetOutlineData(LPTEXTLINE lpTextLine, LPTEXTLINE lpBuf)
{
    TextLine_Copy((LPTEXTLINE)lpTextLine, lpBuf);
    return TRUE;
}


/* TextLine_Draw
 * -------------
 *
 *      Draw a text line object on a DC.
 * Parameters:
 *		hDC		- DC to which the line will be drawn
 *		lpRect	- the object rectangle in logical coordinates
 */
void TextLine_Draw(LPTEXTLINE lpTextLine, HDC hDC, LPRECT lpRect)
{
	RECT rc;
	
	if (!lpTextLine)
		return;
	
	rc = *lpRect;
	rc.left += ((LPLINE)lpTextLine)->m_nTabWidthInHimetric;
	rc.right += ((LPLINE)lpTextLine)->m_nTabWidthInHimetric;
	
    ExtTextOut(
            hDC, 
            rc.left,
			rc.top,
            ETO_CLIPPED | ETO_OPAQUE, 
            (LPRECT)&rc, 
            lpTextLine->m_szText, 
            lpTextLine->m_nLength, 
            (LPINT) NULL /* default char spacing */ 
        );

#if defined( WONT_WORK_IN_METAFILE )
	SaveDC(hDC);
	IntersectClipRect(
			hDC,
			lpRect->left,
			lpRect->top,
			lpRect->right,
			lpRect->bottom
		);
	TabbedTextOut(
            hDC, 
            lpRect->left+((LPLINE)lpTextLine)->m_nTabWidthInHimetric + 
                XformWidthInPixelsToHimetric(NULL, 1), 
            lpRect->top + XformHeightInPixelsToHimetric(NULL, 1),
            lpTextLine->m_szText, 
            lpTextLine->m_nLength, 
			0,
			NULL,
			0
        );
	RestoreDC(hDC, -1);
#endif

}

/* TextLine_DrawSelHilight
 * -----------------------
 *
 *      Handles selection of textline
 */
void TextLine_DrawSelHilight(LPTEXTLINE lpTextLine, HDC hDC, LPRECT lpRect, UINT itemAction, UINT itemState)
{
    if (itemAction & ODA_SELECT) {
        // check if there is a selection state change, ==> invert rect
        if (itemState & ODS_SELECTED) {
            if (!((LPLINE)lpTextLine)->m_fSelected) {
                ((LPLINE)lpTextLine)->m_fSelected = TRUE;
                InvertRect(hDC, (LPRECT)lpRect);        
            }
        } else {
            if (((LPLINE)lpTextLine)->m_fSelected) {
                ((LPLINE)lpTextLine)->m_fSelected = FALSE;
                InvertRect(hDC, lpRect);        
            }
        }
    } else if (itemAction & ODA_DRAWENTIRE) {
        ((LPLINE)lpTextLine)->m_fSelected=((itemState & ODS_SELECTED) ? TRUE : FALSE);
        InvertRect(hDC, lpRect);
    }
}

/* TextLine_Copy
 * -------------
 *
 *      Duplicate a textline
 */
BOOL TextLine_Copy(LPTEXTLINE lpSrcLine, LPTEXTLINE lpDestLine)
{
    _fmemcpy(lpDestLine, lpSrcLine, sizeof(TEXTLINE));
    return TRUE;
}


/* TextLine_CopyToDoc
 * ------------------
 *
 *      Copy a textline to another Document (usually ClipboardDoc)
 */
BOOL TextLine_CopyToDoc(LPTEXTLINE lpSrcLine, LPOUTLINEDOC lpDestDoc, int nIndex)
{
    LPTEXTLINE  lpDestLine;
    BOOL        fStatus = FALSE;
    
    lpDestLine = (LPTEXTLINE) New((DWORD)sizeof(TEXTLINE));
    if (lpDestLine == NULL) {
        OleDbgAssertSz(lpDestLine!=NULL,"Error allocating TextLine");
        return FALSE; 
    }
        
    if (TextLine_Copy(lpSrcLine, lpDestLine)) {
        OutlineDoc_AddLine(lpDestDoc, (LPLINE)lpDestLine, nIndex);
        fStatus = TRUE;
    }
    
    return fStatus;
}


/* TextLine_SaveToStg
 * ------------------
 *
 *      Save a textline into a storage
 *
 *      Return TRUE if successful, FALSE otherwise
 */
BOOL TextLine_SaveToStg(LPTEXTLINE lpTextLine, UINT uFormat, LPSTORAGE lpSrcStg, LPSTORAGE lpDestStg, LPSTREAM lpLLStm, BOOL fRemember)
{
    HRESULT hrErr;
    ULONG nWritten;

    hrErr = lpLLStm->lpVtbl->Write(
            lpLLStm,
            (LPVOID)&lpTextLine->m_nLength,
            sizeof(lpTextLine->m_nLength),
            &nWritten
    );
    if (! OleDbgVerifySz(hrErr == NOERROR, "Error writing TextLine data")) 
        return FALSE;

    hrErr = lpLLStm->lpVtbl->Write(
            lpLLStm,
            (LPVOID)lpTextLine->m_szText,
            lpTextLine->m_nLength,
            &nWritten
    );
    if (! OleDbgVerifySz(hrErr == NOERROR, "Error writing TextLine data")) 
        return FALSE;

    return TRUE;
}


/* TextLine_LoadFromStg
 * --------------------
 *
 *      Load a textline from storage
 */
LPLINE TextLine_LoadFromStg(LPSTORAGE lpSrcStg, LPSTREAM lpLLStm, LPOUTLINEDOC lpDestDoc)
{
    HRESULT hrErr;
    ULONG nRead;
    LPTEXTLINE lpTextLine;

    lpTextLine=(LPTEXTLINE) New((DWORD)sizeof(TEXTLINE));
    if (lpTextLine == NULL) {
        OleDbgAssertSz(lpTextLine!=NULL,"Error allocating TextLine");
        return NULL; 
    }
    
    TextLine_Init(lpTextLine, 0, NULL);

    hrErr = lpLLStm->lpVtbl->Read(
            lpLLStm,
            (LPVOID)&lpTextLine->m_nLength,
            sizeof(lpTextLine->m_nLength),
            &nRead
    );
    if (! OleDbgVerifySz(hrErr == NOERROR, "Error reading TextLine data")) 
        return NULL;

    OleDbgAssert(lpTextLine->m_nLength < sizeof(lpTextLine->m_szText));

    hrErr = lpLLStm->lpVtbl->Read(
            lpLLStm,
            (LPVOID)&lpTextLine->m_szText,
            lpTextLine->m_nLength,
            &nRead
    );
    if (! OleDbgVerifySz(hrErr == NOERROR, "Error reading TextLine data")) 
        return NULL;

    lpTextLine->m_szText[lpTextLine->m_nLength] = '\0'; // add str terminator
    
    return (LPLINE)lpTextLine;
}
