/* $Header$ */

// base WP text object class implementation

/**************** Changes ******************
Date			Edit				Bug					Description
06-Oct-22	rmm11749		ST/WO/2704	Issues with HTML (docview) when printed to a report.
29-Jan-15	rmm8492									Replaced MEMglobal... functions with MEMfree etc (not marked).
03-Apr-12	rmm7472			ST/EC/1290	Fixed crash.
15 NOV 01 mpm_rmm4180							More RTF problems
14-Nov-01	rmm4180									More problems viewing RTF.
22 MAY 00	mpm4700									Implements printing of document
08 NOV 99 mlr0006									Added Rtf support
********************************************/

#ifndef _extcomp_
#include "extcomp.he" // jmg_includecasing
#endif

#ifndef	_WPPARA_HE_
#include "wppara.he" // jmg_includecasing
#endif

#ifndef	_WPDOC_HE_
#include "wpdoc.he" // jmg_includecasing
#endif

#include "htmlctrl.he"	// rmm11749

// ###############################################################################
// ################# WPParagraphClass Public #####################################
// ###############################################################################


WPParagraphClass::WPParagraphClass( WPObjectClass* pParent, WPDocClass* pDocumentObj )
								 :WPObjectClass( pParent, pDocumentObj )
{
	mRows = 0; mRowCount = mRowCountAlloc = 0;
}


WPParagraphClass::~WPParagraphClass()
{
	if (mRows)
		MEMfree(mRows);
}


WPParagraphClass*	WPParagraphClass::make( WPObjectClass* pParent, WPDocClass* pDocumentObj, WPParagraphInfoStruct* pParagraphInfo )
{
	WPParagraphClass* obj = new WPParagraphClass( pParent, pDocumentObj );
	obj->mParagraphInfo = *pParagraphInfo;
	return obj;
}


void WPParagraphClass::initData( WPObjInfoStruct* pObjInfo ,printInfo* pInfo)
{	
	if ( mParent ) mParent->newPara( pObjInfo );
	mRowCount = 0; mInitRow = -1;

	mInitRowInfo = pObjInfo;
	if ( mParagraphInfo.mIndent )
	{
		mInitRowInfo->mOwnerRect.left += WPpointsToDims( mParagraphInfo.mIndent );
		mInitRowInfo->mOwnerRect.width(0);
		mInitRowInfo->mRowRect.left += WPpointsToDims( mParagraphInfo.mIndent );
	}
	WPObjInfoStruct rowInfo = *mInitRowInfo;
	
	rowInfo.mNumber = 1; // always start at row number 1 within a paragraph
	if ( mParagraphInfo.mSpaceBefore )
	{
		qldim dim = WPpointsToDims( mParagraphInfo.mSpaceBefore );
		rowInfo.mOwnerRect.offset( 0, dim );
		rowInfo.mRowRect.offset( 0, dim );
		mInitRowInfo->mOwnerRect.bottom += dim;
	}
	// initialize all child objects
	WPObjectClass::initData( &rowInfo, pInfo);
	newRow( &rowInfo ); // add a final new row
	// finally add paragraph spacing
	// Start rmm4180: empty RTF paragraphs need some spacing
	if (!mFirstChild && !mParagraphInfo.mSpaceBefore && !mParagraphInfo.mSpaceAfter && mDocumentObj && mDocumentObj->isRTF())
	{
		GDItextSpecStruct ts; mParagraphInfo.mTextInfo.getTextSpec(mDocumentObj, &ts);
		//mpm_rmm4180 mParagraphInfo.mSpaceAfter = GDIfontPart(&ts.mFnt, ts.mSty, eFontHeight);
		mInitRowInfo->mOwnerRect.height( GDIfontPart(&ts.mFnt, ts.mSty, eFontHeight ) ); // mpm_rmm4180
	}
	// End rmm4180
	if ( mParagraphInfo.mSpaceAfter )
	{
		qldim dim = WPpointsToDims( mParagraphInfo.mSpaceAfter );
		mInitRowInfo->mOwnerRect.bottom += dim;
	}

	if ( mParagraphInfo.mIndent )
	{
		mInitRowInfo->mRowRect.left -= WPpointsToDims( mParagraphInfo.mIndent );
	}
	if ( mParent )
	{
		mParent->addParaInfo( mInitRowInfo );
		mParent->newPara( mInitRowInfo );
	}
}


void WPParagraphClass::addRowInfo( WPObjInfoStruct* pObjInfo )
{
	if ( mRowCountAlloc == mRowCount )
	{ // make more room
		if (mRows)
			mRows = (WPObjInfoStruct *)MEMrealloc(mRows, (mRowCount + 10) * sizeof(WPObjInfoStruct));
		else
			mRows = (WPObjInfoStruct *)MEMmalloc(10 * sizeof(WPObjInfoStruct));
		if (!mRows) return;
		mRowCountAlloc += 10;
	}

	if ( mInitRow == -1 )
	{
		mInitRow = mRowCount;
		mInitOwnerUnion = pObjInfo->mOwnerRect;
	}
	else
	{
		mInitOwnerUnion.unionRect( &mInitOwnerUnion, &pObjInfo->mOwnerRect );
	}
	if ( mInitRowInfo->mOwnerRect.isEmpty() )
	{
		mInitRowInfo->mOwnerRect = pObjInfo->mOwnerRect;
	}
	else
	{
		mInitRowInfo->mOwnerRect.unionRect( &mInitRowInfo->mOwnerRect, &pObjInfo->mOwnerRect );
	}
	
	mRows[mRowCount] = *pObjInfo;
	mRowCount++;
}


void WPParagraphClass::addParaInfo( WPObjInfoStruct* pObjInfo )
{
	addRowInfo( pObjInfo );
}


void WPParagraphClass::newRow( WPObjInfoStruct* pObjInfo )
{
	// first adjust objects for previous row
	if ( mInitRow != -1 )
	{
		qlong fstInd = mInitRow, lstInd = mRowCount - 1;
		// first find the base line object with the lowest edge (this will be the text object with the largest font)
		// at the same time find the vertical row mode
		qldim bottom = -1; qlong lowest = -1;
		eWPAlign rowAlign = eWPVAbaseline;
		qlong ind;
		for ( ind = fstInd; ind <= lstInd ; ind++ )
		{
			WPObjInfoStruct& row = mRows[ind];
			if ( row.mVAlign != eWPVAbaseline ) rowAlign = row.mVAlign;
			else if ( row.mOwnerRect.bottom > bottom )
			{
				bottom = row.mOwnerRect.bottom;
				lowest = ind;
			}
		}
		// now we can calculate the baseline position for all (based on the largest text object)
		qldim baseLine = 0;
		if ( lowest > -1 )
		{
			WPObjInfoStruct& row = mRows[lowest];
			switch ( rowAlign )
			{
				case eWPVAmiddle: case eWPVAabsmiddle:
					baseLine = row.mOwnerBaseLine + ( mInitOwnerUnion.bottom - row.mOwnerRect.bottom ) / 2;
					break;
				case eWPVAbottom: case eWPVAabsbottom:
					baseLine = row.mOwnerBaseLine + mInitOwnerUnion.bottom - row.mOwnerRect.bottom;
					break;
				default:
					baseLine = row.mOwnerBaseLine;
					break;
			}
		}
		
		// establish horizontal offset based on paragraph alignment
		qldim hoff = 0;
		switch ( mParagraphInfo.mHAlign )
		{
			case eWPHAcenter: hoff = ( mInitRowInfo->mRowRect.right - mInitOwnerUnion.right ) / 2; break;
			case eWPHAright: hoff = mInitRowInfo->mRowRect.right - mInitOwnerUnion.right; break;
		}
		// offset owner rects according to aligments
		for ( ind = fstInd; ind <= lstInd ; ind++ )		// PKHTML qlong ind
		{
			WPObjInfoStruct& row = mRows[ind];
			qldim voff = 0;
			switch ( row.mVAlign )
			{
				case eWPVAtop: case eWPVAtexttop:
					voff = mInitOwnerUnion.top - row.mOwnerRect.top;
					break;
				case eWPVAmiddle: case eWPVAabsmiddle:
					voff = ( mInitOwnerUnion.height() - row.mOwnerRect.height() ) / 2;
					break;
				case eWPVAbottom: case eWPVAabsbottom:
					voff = mInitOwnerUnion.bottom - row.mOwnerRect.bottom;
					break;
				case eWPVAbaseline:
					voff = baseLine - row.mOwnerBaseLine;
					break;
			}
			// all baselines and row rects to be updated
			// update baselines and the objects owner rects
			row.mOwnerBaseLine = baseLine;
			if ( hoff || voff )
			{
				row.mOwnerRect.offset( hoff, voff );
			}
			if ( mInitRowInfo->mOwnerRect.isEmpty() )
			{
				mInitRowInfo->mOwnerRect = row.mOwnerRect;
			}
			else
			{
				mInitRowInfo->mOwnerRect.unionRect( &mInitRowInfo->mOwnerRect, &row.mOwnerRect );
			}
		}
	}

	pObjInfo->mNumber++;
	pObjInfo->mOwnerBaseLine = 0;
	qlrect& childOwnerRect = pObjInfo->mOwnerRect;
	qlrect& childRowRect = pObjInfo->mRowRect;
	childOwnerRect.top = childOwnerRect.bottom = mInitRowInfo->mOwnerRect.bottom + 1;
	childOwnerRect.left = childOwnerRect.right = mInitRowInfo->mRowRect.left; childOwnerRect.right--;
	childRowRect.top = childRowRect.bottom = childOwnerRect.top;
	mInitRow = -1;
}


void WPParagraphClass::paint( WPObjInfoStruct* pObjInfo, WNDpaintStruct* pPaintStruct, qldim pHorzOff, qldim pVertOff )
{
	qlrect paintRect = pPaintStruct->rcPaint;
	qlrect tmpRect;
	
	if (mParagraphInfo.mBullet != eWPbulletNone && mRowCount > 0) // rmm7472: check row count to prevent crash
	{
		qlrect lr; 
		lr.bottom = mRows[0].mOwnerBaseLine;
		lr.right = mRows[0].mOwnerRect.left - 8;
		lr.left = lr.right - 5;
		lr.top = lr.bottom - 5;
		lr.offset( pHorzOff, pVertOff );
		if ( tmpRect.intersectRect( &lr, &paintRect ) )
		{
			qrect r; lr.conv( &r ); HDC& hdc = pPaintStruct->hdc;
			GDItextSpecStruct ts; mParagraphInfo.mTextInfo.getTextSpec( mDocumentObj, &ts );
			switch ( mParagraphInfo.mBullet )
			{
				case eWPbulletCircle:
				case eWPbulletSquare:
				{
					HPEN pen = GDIcreatePen( 1, ts.mTextColor );
					pen = GDIselectObject( hdc, pen );
					if ( mParagraphInfo.mBullet == eWPbulletCircle )
						GDIframeEllipse( hdc, &r );
					else
						GDIframeRect( hdc, &r );
					pen = GDIselectObject( hdc, pen );
					GDIdeleteObject( pen );
					break;
				}
				case eWPbulletDisc:
				{
					GDIsetTextColor( hdc, ts.mTextColor );
					GDIfillEllipse( hdc, &r, GDIgetStockBrush( BLACK_BRUSH ) );
					break;
				}
				default:
				{
					str15 txt; getBulletChars( txt );
					GDIsetTextColor( hdc, ts.mTextColor );
					HFONT fnt = GDIcreateFont( &ts.mFnt, ts.mSty );
					fnt = GDIselectObject( hdc, fnt );
					r.top = r.bottom - GDIfontPart( hdc, eFontAscent ) + 1;
					GDIdrawText( hdc, r.right, r.top, &txt[1], txt.length(), jstRight );
					fnt = GDIselectObject( hdc, fnt );
					GDIdeleteObject( fnt );
					break;
				}
			}
		}
	}

	for ( qshort ind = 0 ; ind < mRowCount ; ind++ )
	{
		tmpRect = mRows[ind].mOwnerRect;
		tmpRect.offset( pHorzOff, pVertOff );
		if ( tmpRect.intersectRect( &tmpRect, &paintRect ) )
			mRows[ind].mOwner->paint( &mRows[ind], pPaintStruct, pHorzOff, pVertOff );
	}
}


qlong WPParagraphClass::message( WPObjInfoStruct* pObjInfo, eWPMessage pMessage, qldim pHorzOff, qldim pVertOff, LPARAM lParam1, LPARAM lParam2 )
{
	qlong result = 0;
	for ( qshort ind = 0 ; ind < mRowCount ; ind++ )
	{
		if ( pMessage == eWPMessFindPt )
		{
			qpoint* pt = (qpoint*)lParam1;
			qlrect tmpRect = mRows[ind].mOwnerRect;
			tmpRect.offset( pHorzOff, pVertOff );
			if ( !tmpRect.ptInRect( pt ) ) continue;
		}
		*pObjInfo = mRows[ind];
		if ((result = pObjInfo->mOwner->message( pObjInfo, pMessage, pHorzOff, pVertOff, lParam1, lParam2 )) != 0) return result;
		else if ( pMessage == eWPMessFindPt )
		{
			pObjInfo->mOwnerRect.offset( pHorzOff, pVertOff );
			pObjInfo->mOwnerBaseLine += pVertOff;
			return 1L;
		}
		else if ( ind+1 == mRowCount && pMessage == eWPMessCopyCharRange )
		{
			WPgetCharRangeStruct* charRangeInfo = (WPgetCharRangeStruct*)lParam1;
			qlrect tmpRect = mRows[ind].mOwnerRect;
			tmpRect.offset( pHorzOff, pVertOff );
			qrect r; tmpRect.conv( &r );
			if ( GDIrectInRegion( charRangeInfo->mSelRgn, &r ) )
			{
				EXTfldval* fldval = (EXTfldval*)lParam2;
				fldval->concat( (qchar)13 );
			}
		}
	}
	return result;
}


void WPParagraphClass::setSpaceBefore( qshort pSpace )
{
	mParagraphInfo.mSpaceBefore = pSpace;
}


void WPParagraphClass::setSpaceAfter( qshort pSpace )
{
	mParagraphInfo.mSpaceAfter = pSpace;
}


// ###############################################################################
// ################# WPParagraphClass Protected ##################################
// ###############################################################################


qbool WPParagraphClass::getNextRow( qlong& pFst, qlong& pLst )
{
	if ( pFst == -1 )
		pFst = pLst = 0;
	else
		pFst = pLst = pLst + 1;
		
	if ( mRowCount > pFst )
	{
		qlong num = mRows[pFst].mNumber;
		while ( mRowCount > (pLst + 1) )
		{
			if ( mRows[pLst + 1].mNumber != num ) break;
			pLst++;
		}
		return qtrue;
	}
	return qfalse;
}


qbool romanInit = qfalse;
str15 romanChars[4];
qshort romanLength[10] = { 0,1,-2,-3,2,1,-2,-3,-4,2 };

void WPParagraphClass::getBulletChars( strxxx& pTxt )
{
	if ( !romanInit )
	{
		romanChars[1] = str15(QTEXT("iiiiviiiix"));
		romanChars[2] = str15(QTEXT("xxxxlxxxxc"));
		romanChars[3] = str15(QTEXT("ccccdccccm"));
	}

	pTxt = qnil;
	switch ( mParagraphInfo.mBullet )
	{
		case eWPbulletUppCase:
		case eWPbulletLowCase:
		{
			qshort num = mParagraphInfo.mNumber;
			while ( num )
			{
				qshort x = ( num-1 ) % 26;
				pTxt.insert( qchar(x) + 'a', 1 );
				num = (num-1) / 26;
			}
			if ( mParagraphInfo.mBullet == eWPbulletUppCase )
				pTxt.upps();
			break;
		}
		case eWPbulletUppCaseRoman:
		case eWPbulletLowCaseRoman:
		{
			str15 num; qlongToString( mParagraphInfo.mNumber, num );
			if ( num.length() > 3 ) num.deleet(1,num.length()-3);
			while ( num.length() )
			{
				qshort ind1 = num.length();
				qshort ind2 = qshort(num[1] - '0');
				qshort len = romanLength[ind2];
				if ( len )
				{
					str15 txt;
					if ( len < 0 )
						txt.copy( romanChars[ind1], ind2 + len + 1, -len );
					else
						txt.copy( romanChars[ind1], ind2, len );
					pTxt.concat( txt );
				}
				num.deleet(1,1);
			}
			if ( mParagraphInfo.mBullet == eWPbulletUppCaseRoman )
				pTxt.upps();
			break;
		}
		case eWPbulletNumeral:
		{
			qlongToString( mParagraphInfo.mNumber, pTxt );
			break;
		}
	}
	pTxt.concat( '.' );
}

qprierr WPParagraphClass::addToPrintJob(  printInfo* pInfo, EXTCompInfo* eci, WPObjInfoStruct* pObjInfo, // mpm4700
																					qldim& pHorzOff, qldim& pVertOff)
{
	qprierr err = PRI_ERR_NONE;
	pInfo->mObj->mTextSpec.mJst = jstNone ;
	switch ( mParagraphInfo.mHAlign )
	{
	case eWPHAleft :
		pInfo->mObj->mTextSpec.mJst = jstLeft;
		break;
	case eWPHAright :
		pInfo->mObj->mTextSpec.mJst = jstRight;
		break;
	case eWPHAcenter :
		pInfo->mObj->mTextSpec.mJst = jstCenter;
		break;
	}
	if ( mParagraphInfo.mBullet != eWPbulletNone )
	{
		qlrect lr; 
		
		lr.bottom = mRows[0].mOwnerBaseLine;
		lr.right = mRows[0].mOwnerRect.left - 8;
		lr.left = lr.right - 5;
		lr.top = lr.bottom - 5;
		lr.offset( pHorzOff, pVertOff );
		EXTfldval fval;
		switch ( mParagraphInfo.mBullet )
		{
			case eWPbulletCircle:
			case eWPbulletDisc:
				pInfo->mObj->mType = PRI_OBJ_OVAL;
			break;
			case eWPbulletSquare:
			pInfo->mObj->mType = PRI_OBJ_ROUNDRECT;
			break;
			//pInfo->mObj->mBorder
			//pInfo->mObj->mLineStyle
			default:
			{
				str15 txt; getBulletChars( txt );
				fval.setChar( txt );
				pInfo->mObj->mData = fval.getFldVal();
				pInfo->mObj->mType = PRI_OBJ_TEXT ;
				if (mRowCount > 0 )
				{
					qlong color;
					mDocumentObj->getDocumentInfo (eWPdocInfoTextColor,0,color);
					pInfo->mObj->mTextSpec.mTextColor =  color ;
					GDItextSpecStruct ts;
					mParagraphInfo.mTextInfo.getTextSpec( mDocumentObj, &ts );
					pInfo->mObj->mTextSpec = ts;
				}
			}
		}
		pInfo->mObj->mBorder.set(WND_BORD_PLAIN,3) ;
		pInfo->mObj->mFillPat = 0 ;
		lr.convto( &pInfo->mObj->mPos );
		err = tqfWPObject::doPRIaddObject( pInfo->mJob, pInfo->mObj );
	}

	for ( qshort ind = 0 ; ind < mRowCount ; ind++ )
	{
		qlrect tmpRect = mRows[ind].mOwnerRect;
		tmpRect.offset( pHorzOff, pVertOff );
		tmpRect.convto( &pInfo->mObj->mPos );
		pInfo->mObj->mMultiLine = qfalse; // mpm_rmm4180
		pInfo->mObj->mHorzExtend = qtrue; // mpm_rmm4180
		err = mRows[ind].mOwner->addToPrintJob( pInfo,eci, &mRows[ind],pHorzOff,pVertOff);
		if ( err >= PRI_ERR_MANAGER_CLOSED ) break;
	}
	return err;
}


/* eof */


