/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"

// INCLUDE ---------------------------------------------------------------

#include <com/sun/star/embed/EmbedMisc.hpp>

#include "scitems.hxx"
#include <editeng/boxitem.hxx>
#include <editeng/brshitem.hxx>
#include <editeng/editdata.hxx>
#include <svtools/colorcfg.hxx>
#include <svx/rotmodit.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/svxfont.hxx>
#include <svx/svdoole2.hxx>
#include <tools/poly.hxx>
#include <vcl/svapp.hxx>
#include <vcl/pdfextoutdevdata.hxx>
#include <svtools/accessibilityoptions.hxx>
#include <svx/framelinkarray.hxx>

#include "output.hxx"
#include "document.hxx"
#include "cell.hxx"
#include "attrib.hxx"
#include "patattr.hxx"
#include "docpool.hxx"
#include "tabvwsh.hxx"
#include "progress.hxx"
#include "pagedata.hxx"
#include "chgtrack.hxx"
#include "chgviset.hxx"
#include "viewutil.hxx"
#include "gridmerg.hxx"
#include "invmerge.hxx"
#include "fillinfo.hxx"
#include "scmod.hxx"
#include "appoptio.hxx"
#include "postit.hxx"

#include <math.h>

using namespace com::sun::star;

// STATIC DATA -----------------------------------------------------------

//	Farben fuer ChangeTracking "nach Autor" wie im Writer (swmodul1.cxx)

#define SC_AUTHORCOLORCOUNT		9

static ColorData nAuthorColor[ SC_AUTHORCOLORCOUNT ] = {
					COL_LIGHTRED, 		COL_LIGHTBLUE,		COL_LIGHTMAGENTA,
					COL_GREEN,			COL_RED,			COL_BLUE,
					COL_BROWN,			COL_MAGENTA,		COL_CYAN };

//------------------------------------------------------------------

ScActionColorChanger::ScActionColorChanger( const ScChangeTrack& rTrack ) :
	rOpt( SC_MOD()->GetAppOptions() ),
	rUsers( rTrack.GetUserCollection() ),
	nLastUserIndex( 0 ),
	nColor( COL_BLACK )
{
}

void ScActionColorChanger::Update( const ScChangeAction& rAction )
{
	ColorData nSetColor;
	switch (rAction.GetType())
	{
		case SC_CAT_INSERT_COLS:
		case SC_CAT_INSERT_ROWS:
		case SC_CAT_INSERT_TABS:
			nSetColor = rOpt.GetTrackInsertColor();
			break;
		case SC_CAT_DELETE_COLS:
		case SC_CAT_DELETE_ROWS:
		case SC_CAT_DELETE_TABS:
			nSetColor = rOpt.GetTrackDeleteColor();
			break;
		case SC_CAT_MOVE:
			nSetColor = rOpt.GetTrackMoveColor();
			break;
		default:
			nSetColor = rOpt.GetTrackContentColor();
			break;
	}
	if ( nSetColor != COL_TRANSPARENT )		// Farbe eingestellt
		nColor = nSetColor;
	else									// nach Autor
	{
		if ( rAction.GetUser() != aLastUserName )
		{
			aLastUserName = rAction.GetUser();
			StrData aData(aLastUserName);
			sal_uInt16 nIndex;
			if (!rUsers.Search(&aData, nIndex))
			{
				// empty string is possible if a name wasn't found while saving a 5.0 file
				DBG_ASSERT( aLastUserName.Len() == 0, "Author not found" );
				nIndex = 0;
			}
			nLastUserIndex = nIndex % SC_AUTHORCOLORCOUNT;
		}
		nColor = nAuthorColor[nLastUserIndex];
	}
}

//==================================================================

ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
                            ScTableInfo& rTabInfo, ScDocument* pNewDoc,
							SCTAB nNewTab, long nNewScrX, long nNewScrY,
							SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
							double nPixelPerTwipsX, double nPixelPerTwipsY,
							const Fraction* pZoomX, const Fraction* pZoomY ) :
	pDev( pNewDev ),
	pRefDevice( pNewDev ),		// default is output device
	pFmtDevice( pNewDev ),		// default is output device
    mrTabInfo( rTabInfo ),
    pRowInfo( rTabInfo.mpRowInfo ),
    nArrCount( rTabInfo.mnArrCount ),
	pDoc( pNewDoc ),
	nTab( nNewTab ),
	nScrX( nNewScrX ),
	nScrY( nNewScrY ),
	nX1( nNewX1 ),
	nY1( nNewY1 ),
	nX2( nNewX2 ),
	nY2( nNewY2 ),
	eType( eNewType ),
	nPPTX( nPixelPerTwipsX ),
	nPPTY( nPixelPerTwipsY ),
	pEditObj( NULL ),
	pViewShell( NULL ),
	pDrawView( NULL ), // #114135#
	bEditMode( sal_False ),
	bMetaFile( sal_False ),
	bSingleGrid( sal_False ),
	bPagebreakMode( sal_False ),
	bSolidBackground( sal_False ),
	bUseStyleColor( sal_False ),
	bForceAutoColor( SC_MOD()->GetAccessOptions().GetIsAutomaticFontColor() ),
	bSyntaxMode( sal_False ),
	pValueColor( NULL ),
	pTextColor( NULL ),
	pFormulaColor( NULL ),
	aGridColor( COL_BLACK ),
	bShowNullValues( sal_True ),
	bShowFormulas( sal_False ),
	bShowSpellErrors( sal_False ),
	bMarkClipped( sal_False ),			// sal_False fuer Drucker/Metafile etc.
	bSnapPixel( sal_False ),
	bAnyRotated( sal_False ),
	bAnyClipped( sal_False ),
	mpTargetPaintWindow(0) // #i74769# use SdrPaintWindow direct
{
	if (pZoomX)
		aZoomX = *pZoomX;
	else
		aZoomX = Fraction(1,1);
	if (pZoomY)
		aZoomY = *pZoomY;
	else
		aZoomY = Fraction(1,1);

	nVisX1 = nX1;
	nVisY1 = nY1;
	nVisX2 = nX2;
	nVisY2 = nY2;
	pDoc->StripHidden( nVisX1, nVisY1, nVisX2, nVisY2, nTab );

	nScrW = 0;
	for (SCCOL nX=nVisX1; nX<=nVisX2; nX++)
		nScrW += pRowInfo[0].pCellInfo[nX+1].nWidth;

	nMirrorW = nScrW;

	nScrH = 0;
	for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
		nScrH += pRowInfo[nArrY].nHeight;

	bTabProtected = pDoc->IsTabProtected( nTab );
	nTabTextDirection = pDoc->GetEditTextDirection( nTab );
	bLayoutRTL = pDoc->IsLayoutRTL( nTab );
}

ScOutputData::~ScOutputData()
{
	delete pValueColor;
	delete pTextColor;
	delete pFormulaColor;
}

void ScOutputData::SetContentDevice( OutputDevice* pContentDev )
{
    // use pContentDev instead of pDev where used

    if ( pRefDevice == pDev )
        pRefDevice = pContentDev;
    if ( pFmtDevice == pDev )
        pFmtDevice = pContentDev;
    pDev = pContentDev;
}

void ScOutputData::SetMirrorWidth( long nNew )
{
	nMirrorW = nNew;
}

void ScOutputData::SetGridColor( const Color& rColor )
{
	aGridColor = rColor;
}

void ScOutputData::SetMarkClipped( sal_Bool bSet )
{
	bMarkClipped = bSet;
}

void ScOutputData::SetShowNullValues( sal_Bool bSet )
{
	bShowNullValues = bSet;
}

void ScOutputData::SetShowFormulas( sal_Bool bSet )
{
	bShowFormulas = bSet;
}

void ScOutputData::SetShowSpellErrors( sal_Bool bSet )
{
	bShowSpellErrors = bSet;
}

void ScOutputData::SetSnapPixel( sal_Bool bSet )
{
	bSnapPixel = bSet;
}

void ScOutputData::SetEditCell( SCCOL nCol, SCROW nRow )
{
	nEditCol = nCol;
	nEditRow = nRow;
	bEditMode = sal_True;
}

void ScOutputData::SetMetaFileMode( sal_Bool bNewMode )
{
	bMetaFile = bNewMode;
}

void ScOutputData::SetSingleGrid( sal_Bool bNewMode )
{
	bSingleGrid = bNewMode;
}

void ScOutputData::SetSyntaxMode( sal_Bool bNewMode )
{
	bSyntaxMode = bNewMode;
	if (bNewMode)
		if (!pValueColor)
		{
			pValueColor = new Color( COL_LIGHTBLUE );
			pTextColor = new Color( COL_BLACK );
			pFormulaColor = new Color( COL_GREEN );
		}
}

void ScOutputData::DrawGrid( sal_Bool bGrid, sal_Bool bPage )
{
	SCCOL nX;
	SCROW nY;
	long nPosX;
	long nPosY;
	SCSIZE nArrY;
    ScBreakType nBreak    = BREAK_NONE;
    ScBreakType nBreakOld = BREAK_NONE;

	sal_Bool bSingle;
	Color aPageColor;
	Color aManualColor;

	if (bPagebreakMode)
		bPage = sal_False;			// keine "normalen" Umbrueche ueber volle Breite/Hoehe

	//!	um den einen Pixel sieht das Metafile (oder die Druck-Ausgabe) anders aus
	//!	als die Bildschirmdarstellung, aber wenigstens passen Druck und Metafile zusammen

	Size aOnePixel = pDev->PixelToLogic(Size(1,1));
	long nOneX = aOnePixel.Width();
	long nOneY = aOnePixel.Height();
	if (bMetaFile)
		nOneX = nOneY = 1;

	long nLayoutSign = bLayoutRTL ? -1 : 1;
	long nSignedOneX = nOneX * nLayoutSign;

	if ( eType == OUTTYPE_WINDOW )
	{
        const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
        aPageColor.SetColor( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor );
        aManualColor.SetColor( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor );
	}
	else
	{
		aPageColor = aGridColor;
		aManualColor = aGridColor;
	}

	pDev->SetLineColor( aGridColor );
	ScGridMerger aGrid( pDev, nOneX, nOneY );

										//
										//	Vertikale Linien
										//

	nPosX = nScrX;
	if ( bLayoutRTL )
		nPosX += nMirrorW - nOneX;

	for (nX=nX1; nX<=nX2; nX++)
	{
		SCCOL nXplus1 = nX+1;
		SCCOL nXplus2 = nX+2;
		sal_uInt16 nWidth = pRowInfo[0].pCellInfo[nXplus1].nWidth;
		if (nWidth)
		{
			nPosX += nWidth * nLayoutSign;

			if ( bPage )
			{
				//	Seitenumbrueche auch in ausgeblendeten suchen
				SCCOL nCol = nXplus1;
				while (nCol <= MAXCOL)
				{
                    nBreak = pDoc->HasColBreak(nCol, nTab);
                    bool bHidden = pDoc->ColHidden(nCol, nTab);

                    if ( nBreak || !bHidden )
						break;
					++nCol;
				}

                if (nBreak != nBreakOld)
				{
					aGrid.Flush();
                    pDev->SetLineColor( (nBreak & BREAK_MANUAL) ? aManualColor :
                                        nBreak ? aPageColor : aGridColor );
                    nBreakOld = nBreak;
				}
			}

			sal_Bool bDraw = bGrid || nBreakOld;	// einfaches Gitter nur wenn eingestellt

			//!	Mit dieser Abfrage wird zuviel weggelassen, wenn ein automatischer
			//!	Umbruch mitten in den Wiederholungsspalten liegt.
			//!	Dann lieber den aeusseren Rahmen zweimal ausgeben...
#if 0
			//	auf dem Drucker die Aussen-Linien weglassen (werden getrennt ausgegeben)
			if ( eType == OUTTYPE_PRINTER && !bMetaFile )
			{
				if ( nX == MAXCOL )
					bDraw = sal_False;
                else if (pDoc->HasColBreak(nXplus1, nTab))
					bDraw = sal_False;
			}
#endif

			sal_uInt16 nWidthXplus2 = pRowInfo[0].pCellInfo[nXplus2].nWidth;
			bSingle = bSingleGrid;									//! in Fillinfo holen !!!!!
			if ( nX<MAXCOL && !bSingle )
			{
				bSingle = ( nWidthXplus2 == 0 );
				for (nArrY=1; nArrY+1<nArrCount && !bSingle; nArrY++)
				{
					if (pRowInfo[nArrY].pCellInfo[nXplus2].bHOverlapped)
						bSingle = sal_True;
					if (pRowInfo[nArrY].pCellInfo[nXplus1].bHideGrid)
						bSingle = sal_True;
				}
			}

			if (bDraw)
			{
				if ( nX<MAXCOL && bSingle )
				{
					SCCOL nVisX = nXplus1;
					while ( nVisX < MAXCOL && !pDoc->GetColWidth(nVisX,nTab) )
						++nVisX;

					nPosY = nScrY;
					long nNextY;
					for (nArrY=1; nArrY+1<nArrCount; nArrY++)
					{
						RowInfo* pThisRowInfo = &pRowInfo[nArrY];
						nNextY = nPosY + pThisRowInfo->nHeight;

						sal_Bool bHOver = pThisRowInfo->pCellInfo[nXplus1].bHideGrid;
						if (!bHOver)
						{
							if (nWidthXplus2)
								bHOver = pThisRowInfo->pCellInfo[nXplus2].bHOverlapped;
							else
							{
								if (nVisX <= nX2)
									bHOver = pThisRowInfo->pCellInfo[nVisX+1].bHOverlapped;
								else
									bHOver = ((ScMergeFlagAttr*)pDoc->GetAttr(
												nVisX,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG))
												->IsHorOverlapped();
								if (bHOver)
									bHOver = ((ScMergeFlagAttr*)pDoc->GetAttr(
												nXplus1,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG))
												->IsHorOverlapped();
							}
						}

						if (pThisRowInfo->bChanged && !bHOver)
						{
							//Point aStart( nPosX-nSignedOneX, nPosY );
							//Point aEnd( nPosX-nSignedOneX, nNextY-nOneY );
							//pDev->DrawLine( aStart, aEnd );
							aGrid.AddVerLine( nPosX-nSignedOneX, nPosY, nNextY-nOneY );
						}
						nPosY = nNextY;
					}
				}
				else
				{
					//Point aStart( nPosX-nSignedOneX, nScrY );
					//Point aEnd( nPosX-nSignedOneX, nScrY+nScrH-nOneY );
					//pDev->DrawLine( aStart, aEnd );
					aGrid.AddVerLine( nPosX-nSignedOneX, nScrY, nScrY+nScrH-nOneY );
				}
			}
		}
	}

										//
										//	Horizontale Linien
										//

    bool bHiddenRow = true;
    SCROW nHiddenEndRow = -1;
	nPosY = nScrY;
	for (nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		SCSIZE nArrYplus1 = nArrY+1;
		nY = pRowInfo[nArrY].nRowNo;
		SCROW nYplus1 = nY+1;
		nPosY += pRowInfo[nArrY].nHeight;

		if (pRowInfo[nArrY].bChanged)
		{
			if ( bPage )
			{
                for (SCROW i = nYplus1; i <= MAXROW; ++i)
                {
                    if (i > nHiddenEndRow)
                        bHiddenRow = pDoc->RowHidden(i, nTab, nHiddenEndRow);
                    /* TODO: optimize the row break thing for large hidden 
                     * segments where HasRowBreak() has to be called 
                     * nevertheless for each row, as a row break is drawn also 
                     * for hidden rows, above them. This needed to be done only 
                     * once per hidden segment, maybe giving manual breaks 
                     * priority. Something like GetNextRowBreak() and 
                     * GetNextManualRowBreak(). */
                    nBreak = pDoc->HasRowBreak(i, nTab);
                    if (!bHiddenRow || nBreak)
                        break;
                }

                if (nBreakOld != nBreak)
				{
					aGrid.Flush();
					pDev->SetLineColor( (nBreak & BREAK_MANUAL) ? aManualColor :
                                        (nBreak) ? aPageColor : aGridColor );
                    nBreakOld = nBreak;
				}
			}

			sal_Bool bDraw = bGrid || nBreakOld;	// einfaches Gitter nur wenn eingestellt

			//!	Mit dieser Abfrage wird zuviel weggelassen, wenn ein automatischer
			//!	Umbruch mitten in den Wiederholungszeilen liegt.
			//!	Dann lieber den aeusseren Rahmen zweimal ausgeben...
#if 0
			//	auf dem Drucker die Aussen-Linien weglassen (werden getrennt ausgegeben)
			if ( eType == OUTTYPE_PRINTER && !bMetaFile )
			{
				if ( nY == MAXROW )
					bDraw = sal_False;
                else if (pDoc->HasRowBreak(nYplus1, nTab))
					bDraw = sal_False;
			}
#endif

			sal_Bool bNextYisNextRow = (pRowInfo[nArrYplus1].nRowNo == nYplus1);
			bSingle = !bNextYisNextRow;				// Hidden
			for (SCCOL i=nX1; i<=nX2 && !bSingle; i++)
			{
				if (pRowInfo[nArrYplus1].pCellInfo[i+1].bVOverlapped)
					bSingle = sal_True;
			}

			if (bDraw)
			{
				if ( bSingle && nY<MAXROW )
				{
					SCROW nVisY = pRowInfo[nArrYplus1].nRowNo;

					nPosX = nScrX;
					if ( bLayoutRTL )
						nPosX += nMirrorW - nOneX;

					long nNextX;
					for (SCCOL i=nX1; i<=nX2; i++)
					{
						nNextX = nPosX + pRowInfo[0].pCellInfo[i+1].nWidth * nLayoutSign;
						if (nNextX != nPosX)								// sichtbar
						{
							sal_Bool bVOver;
							if ( bNextYisNextRow )
								bVOver = pRowInfo[nArrYplus1].pCellInfo[i+1].bVOverlapped;
							else
							{
								bVOver = ((ScMergeFlagAttr*)pDoc->GetAttr(
											i,nYplus1,nTab,ATTR_MERGE_FLAG))
											->IsVerOverlapped()
									&& 	 ((ScMergeFlagAttr*)pDoc->GetAttr(
											i,nVisY,nTab,ATTR_MERGE_FLAG))
											->IsVerOverlapped();
									//! nVisY aus Array ??
							}
							if (!bVOver)
							{
								//Point aStart( nPosX, nPosY-nOneY );
								//Point aEnd( nNextX-nSignedOneX, nPosY-nOneY );
								//pDev->DrawLine( aStart, aEnd );
								aGrid.AddHorLine( nPosX, nNextX-nSignedOneX, nPosY-nOneY );
							}
						}
						nPosX = nNextX;
					}
				}
				else
				{
					//Point aStart( nScrX, nPosY-nOneY );
					//Point aEnd( nScrX+nScrW-nOneX, nPosY-nOneY );
					//pDev->DrawLine( aStart, aEnd );
					aGrid.AddHorLine( nScrX, nScrX+nScrW-nOneX, nPosY-nOneY );
				}
			}
		}
	}
}

//	----------------------------------------------------------------------------

void ScOutputData::SetPagebreakMode( ScPageBreakData* pPageData )
{
	bPagebreakMode = sal_True;
	if (!pPageData)
		return;						// noch nicht initialisiert -> alles "nicht gedruckt"

	//	gedruckten Bereich markieren
	//	(in FillInfo ist schon alles auf sal_False initialisiert)

    sal_uInt16 nRangeCount = sal::static_int_cast<sal_uInt16>(pPageData->GetCount());
	for (sal_uInt16 nPos=0; nPos<nRangeCount; nPos++)
	{
		ScRange aRange = pPageData->GetData( nPos ).GetPrintRange();

		SCCOL nStartX = Max( aRange.aStart.Col(), nX1 );
		SCCOL nEndX   = Min( aRange.aEnd.Col(),   nX2 );
		SCROW nStartY = Max( aRange.aStart.Row(), nY1 );
		SCROW nEndY   = Min( aRange.aEnd.Row(),   nY2 );

		for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
		{
			RowInfo* pThisRowInfo = &pRowInfo[nArrY];
			if ( pThisRowInfo->bChanged && pThisRowInfo->nRowNo >= nStartY &&
										   pThisRowInfo->nRowNo <= nEndY )
			{
				for (SCCOL nX=nStartX; nX<=nEndX; nX++)
					pThisRowInfo->pCellInfo[nX+1].bPrinted = sal_True;
			}
		}
	}
}

void ScOutputData::FindRotated()
{
	//!	nRotMax speichern
	SCCOL nRotMax = nX2;
	for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
		if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
			nRotMax = pRowInfo[nRotY].nRotMaxCol;

	for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		if ( pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE &&
			 ( pThisRowInfo->bChanged || pRowInfo[nArrY-1].bChanged ||
			   ( nArrY+1<nArrCount && pRowInfo[nArrY+1].bChanged ) ) )
		{
			SCROW nY = pThisRowInfo->nRowNo;

			for (SCCOL nX=0; nX<=nRotMax; nX++)
			{
				CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];
				const ScPatternAttr* pPattern = pInfo->pPatternAttr;
				const SfxItemSet* pCondSet = pInfo->pConditionSet;

                if ( !pPattern && !pDoc->ColHidden(nX, nTab) )
				{
					pPattern = pDoc->GetPattern( nX, nY, nTab );
					pCondSet = pDoc->GetCondResult( nX, nY, nTab );
				}

				if ( pPattern )		// Spalte nicht ausgeblendet
				{
					sal_uInt8 nDir = pPattern->GetRotateDir( pCondSet );
					if (nDir != SC_ROTDIR_NONE)
					{
						pInfo->nRotateDir = nDir;
						bAnyRotated = sal_True;
					}
				}
			}
		}
	}
}

//	----------------------------------------------------------------------------

sal_uInt16 lcl_GetRotateDir( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
{
	const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
	const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );

	sal_uInt16 nRet = SC_ROTDIR_NONE;

	long nAttrRotate = pPattern->GetRotateVal( pCondSet );
	if ( nAttrRotate )
	{
		SvxRotateMode eRotMode = (SvxRotateMode)((const SvxRotateModeItem&)
					pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet)).GetValue();

		if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
			nRet = SC_ROTDIR_STANDARD;
		else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
			nRet = SC_ROTDIR_CENTER;
		else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
		{
			long nRot180 = nAttrRotate % 18000;		// 1/100 Grad
			if ( nRot180 == 9000 )
				nRet = SC_ROTDIR_CENTER;
			else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) ||
					  ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) )
				nRet = SC_ROTDIR_LEFT;
			else
				nRet = SC_ROTDIR_RIGHT;
		}
	}

	return nRet;
}

const SvxBrushItem* lcl_FindBackground( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
{
	const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
	const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
	const SvxBrushItem* pBackground = (const SvxBrushItem*)
							&pPattern->GetItem( ATTR_BACKGROUND, pCondSet );

	sal_uInt16 nDir = lcl_GetRotateDir( pDoc, nCol, nRow, nTab );

	//	CENTER wird wie RIGHT behandelt...
	if ( nDir == SC_ROTDIR_RIGHT || nDir == SC_ROTDIR_CENTER )
	{
		//	Text geht nach rechts -> Hintergrund von links nehmen
		while ( nCol > 0 && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
							pBackground->GetColor().GetTransparency() != 255 )
		{
			--nCol;
			pPattern = pDoc->GetPattern( nCol, nRow, nTab );
			pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
			pBackground = (const SvxBrushItem*)&pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
		}
	}
	else if ( nDir == SC_ROTDIR_LEFT )
	{
		//	Text geht nach links -> Hintergrund von rechts nehmen
		while ( nCol < MAXCOL && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
							pBackground->GetColor().GetTransparency() != 255 )
		{
			++nCol;
			pPattern = pDoc->GetPattern( nCol, nRow, nTab );
			pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
			pBackground = (const SvxBrushItem*)&pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
		}
	}

	return pBackground;
}

//	----------------------------------------------------------------------------

sal_Bool lcl_EqualBack( const RowInfo& rFirst, const RowInfo& rOther,
					SCCOL nX1, SCCOL nX2, sal_Bool bShowProt, sal_Bool bPagebreakMode )
{
	if ( rFirst.bChanged   != rOther.bChanged ||
		 rFirst.bEmptyBack != rOther.bEmptyBack )
		return sal_False;

	SCCOL nX;
	if ( bShowProt )
	{
		for ( nX=nX1; nX<=nX2; nX++ )
		{
			const ScPatternAttr* pPat1 = rFirst.pCellInfo[nX+1].pPatternAttr;
			const ScPatternAttr* pPat2 = rOther.pCellInfo[nX+1].pPatternAttr;
			if ( !pPat1 || !pPat2 ||
					&pPat1->GetItem(ATTR_PROTECTION) != &pPat2->GetItem(ATTR_PROTECTION) )
				return sal_False;
		}
	}
	else
	{
		for ( nX=nX1; nX<=nX2; nX++ )
			if ( rFirst.pCellInfo[nX+1].pBackground != rOther.pCellInfo[nX+1].pBackground )
				return sal_False;
	}

	if ( rFirst.nRotMaxCol != SC_ROTMAX_NONE || rOther.nRotMaxCol != SC_ROTMAX_NONE )
		for ( nX=nX1; nX<=nX2; nX++ )
			if ( rFirst.pCellInfo[nX+1].nRotateDir != rOther.pCellInfo[nX+1].nRotateDir )
				return sal_False;

	if ( bPagebreakMode )
		for ( nX=nX1; nX<=nX2; nX++ )
			if ( rFirst.pCellInfo[nX+1].bPrinted != rOther.pCellInfo[nX+1].bPrinted )
				return sal_False;

	return sal_True;
}

void ScOutputData::DrawBackground()
{
	FindRotated();				//! von aussen ?

	ScModule* pScMod = SC_MOD();

	// used only if bSolidBackground is set (only for ScGridWindow):
    Color aBgColor( pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );

	Rectangle aRect;
	Size aOnePixel = pDev->PixelToLogic(Size(1,1));
	long nOneX = aOnePixel.Width();
	long nOneY = aOnePixel.Height();

	if (bMetaFile)
		nOneX = nOneY = 0;

	long nLayoutSign = bLayoutRTL ? -1 : 1;
	long nSignedOneX = nOneX * nLayoutSign;

	pDev->SetLineColor();

	sal_Bool bShowProt = bSyntaxMode && pDoc->IsTabProtected(nTab);
	sal_Bool bDoAll = bShowProt || bPagebreakMode || bSolidBackground;

	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	sal_Bool bCellContrast = bUseStyleColor &&
			Application::GetSettings().GetStyleSettings().GetHighContrastMode();

	long nPosY = nScrY;
	for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		long nRowHeight = pThisRowInfo->nHeight;

		if ( pThisRowInfo->bChanged )
		{
			if ( ( ( pThisRowInfo->bEmptyBack ) || bSyntaxMode ) && !bDoAll )
			{
				//	nichts
			}
			else
			{
				// scan for rows with the same background:
				SCSIZE nSkip = 0;
				while ( nArrY+nSkip+2<nArrCount &&
						lcl_EqualBack( *pThisRowInfo, pRowInfo[nArrY+nSkip+1],
										nX1, nX2, bShowProt, bPagebreakMode ) )
				{
					++nSkip;
					nRowHeight += pRowInfo[nArrY+nSkip].nHeight;	// after incrementing
				}

				long nPosX = nScrX;
				if ( bLayoutRTL )
					nPosX += nMirrorW - nOneX;
				aRect = Rectangle( nPosX,nPosY, nPosX,nPosY+nRowHeight-nOneY );

				const SvxBrushItem* pOldBackground = NULL;
				const SvxBrushItem* pBackground;
				for (SCCOL nX=nX1; nX<=nX2; nX++)
				{
					CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];

					if (bCellContrast)
					{
						//	high contrast for cell borders and backgrounds -> empty background
						pBackground = ScGlobal::GetEmptyBrushItem();
					}
					else if (bShowProt)			// show cell protection in syntax mode
					{
						const ScPatternAttr* pP = pInfo->pPatternAttr;
						if (pP)
						{
							const ScProtectionAttr& rProt = (const ScProtectionAttr&)
																pP->GetItem(ATTR_PROTECTION);
							if (rProt.GetProtection() || rProt.GetHideCell())
								pBackground = ScGlobal::GetProtectedBrushItem();
							else
								pBackground = ScGlobal::GetEmptyBrushItem();
						}
						else
							pBackground = NULL;
					}
					else
						pBackground = pInfo->pBackground;

					if ( bPagebreakMode && !pInfo->bPrinted )
						pBackground = ScGlobal::GetProtectedBrushItem();

					if ( pInfo->nRotateDir > SC_ROTDIR_STANDARD &&
							pBackground->GetColor().GetTransparency() != 255 &&
							!bCellContrast )
					{
						SCROW nY = pRowInfo[nArrY].nRowNo;
						pBackground = lcl_FindBackground( pDoc, nX, nY, nTab );
					}

					if ( pBackground != pOldBackground )
					{
						aRect.Right() = nPosX-nSignedOneX;
						if (pOldBackground)				// ==0 if hidden
						{
							Color aBackCol = pOldBackground->GetColor();
							if ( bSolidBackground && aBackCol.GetTransparency() )
								aBackCol = aBgColor;
							if ( !aBackCol.GetTransparency() )		//! partial transparency?
							{
								pDev->SetFillColor( aBackCol );
								pDev->DrawRect( aRect );
							}
						}
						aRect.Left() = nPosX;
						pOldBackground = pBackground;
					}
					nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
				}
				aRect.Right() = nPosX-nSignedOneX;
				if (pOldBackground)
				{
					Color aBackCol = pOldBackground->GetColor();
					if ( bSolidBackground && aBackCol.GetTransparency() )
						aBackCol = aBgColor;
					if ( !aBackCol.GetTransparency() )		//! partial transparency?
					{
						pDev->SetFillColor( aBackCol );
						pDev->DrawRect( aRect );
					}
				}

				nArrY += nSkip;
			}
		}
		nPosY += nRowHeight;
	}
}

void ScOutputData::DrawShadow()
{
	DrawExtraShadow( sal_False, sal_False, sal_False, sal_False );
}

void ScOutputData::DrawExtraShadow(sal_Bool bLeft, sal_Bool bTop, sal_Bool bRight, sal_Bool bBottom)
{
	pDev->SetLineColor();

	const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	sal_Bool bCellContrast = bUseStyleColor && rStyleSettings.GetHighContrastMode();
	Color aAutoTextColor;
	if ( bCellContrast )
        aAutoTextColor.SetColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
	{
		Size aOnePixel = pDev->PixelToLogic(Size(1,1));
		long nOneX = aOnePixel.Width();
		nInitPosX += nMirrorW - nOneX;
	}
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	long nPosY = nScrY - pRowInfo[0].nHeight;
	for (SCSIZE nArrY=0; nArrY<nArrCount; nArrY++)
	{
		sal_Bool bCornerY = ( nArrY == 0 ) || ( nArrY+1 == nArrCount );
		sal_Bool bSkipY = ( nArrY==0 && !bTop ) || ( nArrY+1 == nArrCount && !bBottom );

		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		long nRowHeight = pThisRowInfo->nHeight;

		if ( pThisRowInfo->bChanged && !bSkipY )
		{
			long nPosX = nInitPosX - pRowInfo[0].pCellInfo[nX1].nWidth * nLayoutSign;
			for (SCCOL nArrX=nX1; nArrX<=nX2+2; nArrX++)
			{
				sal_Bool bCornerX = ( nArrX==nX1 || nArrX==nX2+2 );
				sal_Bool bSkipX = ( nArrX==nX1 && !bLeft ) || ( nArrX==nX2+2 && !bRight );

				for (sal_uInt16 nPass=0; nPass<2; nPass++)			// horizontal / vertikal
				{
					const SvxShadowItem* pAttr = nPass ?
							pThisRowInfo->pCellInfo[nArrX].pVShadowOrigin :
							pThisRowInfo->pCellInfo[nArrX].pHShadowOrigin;
					if ( pAttr && !bSkipX )
					{
						ScShadowPart ePart = nPass ?
								pThisRowInfo->pCellInfo[nArrX].eVShadowPart :
								pThisRowInfo->pCellInfo[nArrX].eHShadowPart;

						sal_Bool bDo = sal_True;
						if ( (nPass==0 && bCornerX) || (nPass==1 && bCornerY) )
							if ( ePart != SC_SHADOW_CORNER )
								bDo = sal_False;

						if (bDo)
						{
							long nThisWidth = pRowInfo[0].pCellInfo[nArrX].nWidth;
							long nMaxWidth = nThisWidth;
							if (!nMaxWidth)
							{
								//!	direction must depend on shadow location
								SCCOL nWx = nArrX;		// nX+1
								while (nWx<nX2 && !pRowInfo[0].pCellInfo[nWx+1].nWidth)
									++nWx;
								nMaxWidth = pRowInfo[0].pCellInfo[nWx+1].nWidth;
							}

//							Rectangle aRect( Point(nPosX,nPosY),
//											 Size( pRowInfo[0].pCellInfo[nArrX].nWidth,
//													pRowInfo[nArrY].nHeight ) );

							// rectangle is in logical orientation
							Rectangle aRect( nPosX, nPosY,
											 nPosX + ( nThisWidth - 1 ) * nLayoutSign,
											 nPosY + pRowInfo[nArrY].nHeight - 1 );

							long nSize = pAttr->GetWidth();
							long nSizeX = (long)(nSize*nPPTX);
							if (nSizeX >= nMaxWidth) nSizeX = nMaxWidth-1;
							long nSizeY = (long)(nSize*nPPTY);
							if (nSizeY >= nRowHeight) nSizeY = nRowHeight-1;

							nSizeX *= nLayoutSign;		// used only to add to rectangle values

							SvxShadowLocation eLoc = pAttr->GetLocation();
							if ( bLayoutRTL )
							{
								//	Shadow location is specified as "visual" (right is always right),
								//	so the attribute's location value is mirrored here and in FillInfo.
								switch (eLoc)
								{
									case SVX_SHADOW_BOTTOMRIGHT: eLoc = SVX_SHADOW_BOTTOMLEFT;	break;
									case SVX_SHADOW_BOTTOMLEFT:	 eLoc = SVX_SHADOW_BOTTOMRIGHT;	break;
									case SVX_SHADOW_TOPRIGHT:	 eLoc = SVX_SHADOW_TOPLEFT;		break;
									case SVX_SHADOW_TOPLEFT:	 eLoc = SVX_SHADOW_TOPRIGHT;	break;
                                    default:
                                    {
                                        // added to avoid warnings
                                    }
								}
							}

							if (ePart == SC_SHADOW_HORIZ || ePart == SC_SHADOW_HSTART ||
								ePart == SC_SHADOW_CORNER)
							{
								if (eLoc == SVX_SHADOW_TOPLEFT || eLoc == SVX_SHADOW_TOPRIGHT)
									aRect.Top() = aRect.Bottom() - nSizeY;
								else
									aRect.Bottom() = aRect.Top() + nSizeY;
							}
							if (ePart == SC_SHADOW_VERT || ePart == SC_SHADOW_VSTART ||
								ePart == SC_SHADOW_CORNER)
							{
								if (eLoc == SVX_SHADOW_TOPLEFT || eLoc == SVX_SHADOW_BOTTOMLEFT)
									aRect.Left() = aRect.Right() - nSizeX;
								else
									aRect.Right() = aRect.Left() + nSizeX;
							}
							if (ePart == SC_SHADOW_HSTART)
							{
								if (eLoc == SVX_SHADOW_TOPLEFT || eLoc == SVX_SHADOW_BOTTOMLEFT)
									aRect.Right() -= nSizeX;
								else
									aRect.Left() += nSizeX;
							}
							if (ePart == SC_SHADOW_VSTART)
							{
								if (eLoc == SVX_SHADOW_TOPLEFT || eLoc == SVX_SHADOW_TOPRIGHT)
									aRect.Bottom() -= nSizeY;
								else
									aRect.Top() += nSizeY;
							}

							//! merge rectangles?
							pDev->SetFillColor( bCellContrast ? aAutoTextColor : pAttr->GetColor() );
							pDev->DrawRect( aRect );
						}
					}
				}

				nPosX += pRowInfo[0].pCellInfo[nArrX].nWidth * nLayoutSign;
			}
		}
		nPosY += nRowHeight;
	}
}

//
//	Loeschen
//

void ScOutputData::DrawClear()
{
	Rectangle aRect;
	Size aOnePixel = pDev->PixelToLogic(Size(1,1));
	long nOneX = aOnePixel.Width();
	long nOneY = aOnePixel.Height();

	// (called only for ScGridWindow)
    Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );

	if (bMetaFile)
		nOneX = nOneY = 0;

	pDev->SetLineColor();

	pDev->SetFillColor( aBgColor );

	long nPosY = nScrY;
	for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		long nRowHeight = pThisRowInfo->nHeight;

		if ( pThisRowInfo->bChanged )
		{
			// scan for more rows which must be painted:
			SCSIZE nSkip = 0;
			while ( nArrY+nSkip+2<nArrCount && pRowInfo[nArrY+nSkip+1].bChanged )
			{
				++nSkip;
				nRowHeight += pRowInfo[nArrY+nSkip].nHeight;	// after incrementing
			}

			aRect = Rectangle( Point( nScrX, nPosY ),
					Size( nScrW+1-nOneX, nRowHeight+1-nOneY) );
			pDev->DrawRect( aRect );

			nArrY += nSkip;
		}
		nPosY += nRowHeight;
	}
}


//
//	Linien
//

long lclGetSnappedX( OutputDevice& rDev, long nPosX, bool bSnapPixel )
{
    return (bSnapPixel && nPosX) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( nPosX, 0 ) ) ).Width() : nPosX;
}

long lclGetSnappedY( OutputDevice& rDev, long nPosY, bool bSnapPixel )
{
    return (bSnapPixel && nPosY) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( 0, nPosY ) ) ).Height() : nPosY;
}

size_t lclGetArrayColFromCellInfoX( sal_uInt16 nCellInfoX, sal_uInt16 nCellInfoFirstX, sal_uInt16 nCellInfoLastX, bool bRTL )
{
    return static_cast< size_t >( bRTL ? (nCellInfoLastX + 2 - nCellInfoX) : (nCellInfoX - nCellInfoFirstX) );
}

void ScOutputData::DrawFrame()
{
	sal_uLong nOldDrawMode = pDev->GetDrawMode();

	Color aSingleColor;
	sal_Bool bUseSingleColor = sal_False;
	const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	sal_Bool bCellContrast = bUseStyleColor && rStyleSettings.GetHighContrastMode();

	//	#107519# if a Calc OLE object is embedded in Draw/Impress, the VCL DrawMode is used
	//	for display mode / B&W printing. The VCL DrawMode handling doesn't work for lines
	//	that are drawn with DrawRect, so if the line/background bits are set, the DrawMode
	//	must be reset and the border colors handled here.

	if ( ( nOldDrawMode & DRAWMODE_WHITEFILL ) && ( nOldDrawMode & DRAWMODE_BLACKLINE ) )
	{
		pDev->SetDrawMode( nOldDrawMode & (~DRAWMODE_WHITEFILL) );
		aSingleColor.SetColor( COL_BLACK );
		bUseSingleColor = sal_True;
	}
	else if ( ( nOldDrawMode & DRAWMODE_SETTINGSFILL ) && ( nOldDrawMode & DRAWMODE_SETTINGSLINE ) )
	{
		pDev->SetDrawMode( nOldDrawMode & (~DRAWMODE_SETTINGSFILL) );
		aSingleColor = rStyleSettings.GetWindowTextColor();		// same as used in VCL for DRAWMODE_SETTINGSLINE
		bUseSingleColor = sal_True;
	}
	else if ( bCellContrast )
	{
		aSingleColor.SetColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
		bUseSingleColor = sal_True;
	}

    const Color* pForceColor = bUseSingleColor ? &aSingleColor : 0;

	if (bAnyRotated)
		DrawRotatedFrame( pForceColor );		// removes the lines that must not be painted here

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
	{
		Size aOnePixel = pDev->PixelToLogic(Size(1,1));
		long nOneX = aOnePixel.Width();
		nInitPosX += nMirrorW - nOneX;
	}
	long nLayoutSign = bLayoutRTL ? -1 : 1;


    // *** set column and row sizes of the frame border array ***

    svx::frame::Array& rArray = mrTabInfo.maArray;
    size_t nColCount = rArray.GetColCount();
    size_t nRowCount = rArray.GetRowCount();

    // row heights

    // row 0 is not visible (dummy for borders from top) - subtract its height from initial position
    // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit before
    long nOldPosY = nScrY - 1 - pRowInfo[ 0 ].nHeight;
    long nOldSnapY = lclGetSnappedY( *pDev, nOldPosY, bSnapPixel );
    rArray.SetYOffset( nOldSnapY );
    for( size_t nRow = 0; nRow < nRowCount; ++nRow )
    {
        long nNewPosY = nOldPosY + pRowInfo[ nRow ].nHeight;
        long nNewSnapY = lclGetSnappedY( *pDev, nNewPosY, bSnapPixel );
        rArray.SetRowHeight( nRow, nNewSnapY - nOldSnapY );
        nOldPosY = nNewPosY;
        nOldSnapY = nNewSnapY;
    }

    // column widths

    // column nX1 is not visible (dummy for borders from left) - subtract its width from initial position
    // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit above
    long nOldPosX = nInitPosX - nLayoutSign * (1 + pRowInfo[ 0 ].pCellInfo[ nX1 ].nWidth);
    long nOldSnapX = lclGetSnappedX( *pDev, nOldPosX, bSnapPixel );
    // set X offset for left-to-right sheets; for right-to-left sheets this is done after for() loop
    if( !bLayoutRTL )
        rArray.SetXOffset( nOldSnapX );
    for( sal_uInt16 nInfoIdx = nX1; nInfoIdx <= nX2 + 2; ++nInfoIdx )
    {
        size_t nCol = lclGetArrayColFromCellInfoX( nInfoIdx, nX1, nX2, bLayoutRTL );
        long nNewPosX = nOldPosX + pRowInfo[ 0 ].pCellInfo[ nInfoIdx ].nWidth * nLayoutSign;
        long nNewSnapX = lclGetSnappedX( *pDev, nNewPosX, bSnapPixel );
        rArray.SetColWidth( nCol, Abs( nNewSnapX - nOldSnapX ) );
        nOldPosX = nNewPosX;
        nOldSnapX = nNewSnapX;
    }
    if( bLayoutRTL )
        rArray.SetXOffset( nOldSnapX );

    // *** draw the array ***

    size_t nFirstCol = 1;
    size_t nFirstRow = 1;
    size_t nLastCol = nColCount - 2;
    size_t nLastRow = nRowCount - 2;

    if( mrTabInfo.mbPageMode )
        rArray.SetClipRange( nFirstCol, nFirstRow, nLastCol, nLastRow );

    // draw only rows with set RowInfo::bChanged flag
    size_t nRow1 = nFirstRow;
    while( nRow1 <= nLastRow )
    {
        while( (nRow1 <= nLastRow) && !pRowInfo[ nRow1 ].bChanged ) ++nRow1;
        if( nRow1 <= nLastRow )
        {
            size_t nRow2 = nRow1;
            while( (nRow2 + 1 <= nLastRow) && pRowInfo[ nRow2 + 1 ].bChanged ) ++nRow2;
            rArray.DrawRange( *pDev, nFirstCol, nRow1, nLastCol, nRow2, pForceColor );
            nRow1 = nRow2 + 1;
        }
    }

	pDev->SetDrawMode(nOldDrawMode);
}

//	-------------------------------------------------------------------------

//	Linie unter der Zelle

const SvxBorderLine* lcl_FindHorLine( ScDocument* pDoc,
						SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nRotDir,
						sal_Bool bTopLine )
{
	if ( nRotDir != SC_ROTDIR_LEFT && nRotDir != SC_ROTDIR_RIGHT )
		return NULL;

	sal_Bool bFound = sal_False;
	while (!bFound)
	{
		if ( nRotDir == SC_ROTDIR_LEFT )
		{
			//	Text nach links -> Linie von rechts
			if ( nCol < MAXCOL )
				++nCol;
			else
				return NULL;				// war nix
		}
		else
		{
			//	Text nach rechts -> Linie von links
			if ( nCol > 0 )
				--nCol;
			else
				return NULL;				// war nix
		}
		const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
		const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
		if ( !pPattern->GetRotateVal( pCondSet ) ||
				((const SvxRotateModeItem&)pPattern->GetItem(
					ATTR_ROTATE_MODE, pCondSet)).GetValue() == SVX_ROTATE_MODE_STANDARD )
			bFound = sal_True;
	}

	if (bTopLine)
		--nRow;
	const SvxBorderLine* pThisBottom;
	if ( ValidRow(nRow) )
		pThisBottom = ((const SvxBoxItem*)pDoc->GetAttr( nCol, nRow, nTab, ATTR_BORDER ))->GetBottom();
	else
		pThisBottom = NULL;
	const SvxBorderLine* pNextTop;
	if ( nRow < MAXROW )
		pNextTop = ((const SvxBoxItem*)pDoc->GetAttr( nCol, nRow+1, nTab, ATTR_BORDER ))->GetTop();
	else
		pNextTop = NULL;

	if ( ScHasPriority( pThisBottom, pNextTop ) )
		return pThisBottom;
	else
		return pNextTop;
}

// lcl_HorizLine muss genau zu normal ausgegebenen Linien passen!

void lcl_HorizLine( OutputDevice& rDev, const Point& rLeft, const Point& rRight,
                    const svx::frame::Style& rLine, const Color* pForceColor )
{
    svx::frame::DrawHorFrameBorder( rDev, rLeft, rRight, rLine, pForceColor );
}

void lcl_VertLineEnds( OutputDevice& rDev, const Point& rTop, const Point& rBottom,
        const Color& rColor, long nXOffs, long nWidth,
        const svx::frame::Style& rTopLine, const svx::frame::Style& rBottomLine )
{
    rDev.SetLineColor(rColor);              // PEN_NULL ???
    rDev.SetFillColor(rColor);

	//	Position oben/unten muss unabhaengig von der Liniendicke sein,
	//	damit der Winkel stimmt (oder X-Position auch anpassen)
	long nTopPos = rTop.Y();
	long nBotPos = rBottom.Y();

    long nTopLeft = rTop.X() + nXOffs;
    long nTopRight = nTopLeft + nWidth - 1;

    long nBotLeft = rBottom.X() + nXOffs;
    long nBotRight = nBotLeft + nWidth - 1;

	//	oben abschliessen

    if ( rTopLine.Prim() )
	{
        long nLineW = rTopLine.GetWidth();
        if (nLineW >= 2)
		{
			Point aTriangle[3];
			aTriangle[0] = Point( nTopLeft, nTopPos );		// wie aPoints[0]
			aTriangle[1] = Point( nTopRight, nTopPos );		// wie aPoints[1]
            aTriangle[2] = Point( rTop.X(), nTopPos - (nLineW - 1) / 2 );
			Polygon aTriPoly( 3, aTriangle );
            rDev.DrawPolygon( aTriPoly );
		}
	}

	//	unten abschliessen

    if ( rBottomLine.Prim() )
	{
        long nLineW = rBottomLine.GetWidth();
        if (nLineW >= 2)
		{
			Point aTriangle[3];
			aTriangle[0] = Point( nBotLeft, nBotPos );		// wie aPoints[3]
			aTriangle[1] = Point( nBotRight, nBotPos );		// wie aPoints[2]
            aTriangle[2] = Point( rBottom.X(), nBotPos - (nLineW - 1) / 2 + nLineW - 1 );
			Polygon aTriPoly( 3, aTriangle );
            rDev.DrawPolygon( aTriPoly );
		}
	}
}

void lcl_VertLine( OutputDevice& rDev, const Point& rTop, const Point& rBottom,
                    const svx::frame::Style& rLine,
                    const svx::frame::Style& rTopLine, const svx::frame::Style& rBottomLine,
                    const Color* pForceColor )
{
    if( rLine.Prim() )
    {
        svx::frame::DrawVerFrameBorderSlanted( rDev, rTop, rBottom, rLine, pForceColor );

        svx::frame::Style aScaled( rLine );
        aScaled.ScaleSelf( 1.0 / cos( svx::frame::GetVerDiagAngle( rTop, rBottom ) ) );
        if( pForceColor )
            aScaled.SetColor( *pForceColor );

        long nXOffs = (aScaled.GetWidth() - 1) / -2L;

        lcl_VertLineEnds( rDev, rTop, rBottom, aScaled.GetColor(),
            nXOffs, aScaled.Prim(), rTopLine, rBottomLine );

        if( aScaled.Secn() )
            lcl_VertLineEnds( rDev, rTop, rBottom, aScaled.GetColor(),
                nXOffs + aScaled.Prim() + aScaled.Dist(), aScaled.Secn(), rTopLine, rBottomLine );
    }
}

void ScOutputData::DrawRotatedFrame( const Color* pForceColor )
{
	//!	nRotMax speichern
	SCCOL nRotMax = nX2;
	for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
		if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
			nRotMax = pRowInfo[nRotY].nRotMaxCol;

	const ScPatternAttr* pPattern;
	const SfxItemSet*	 pCondSet;

	const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	sal_Bool bCellContrast = bUseStyleColor && rStyleSettings.GetHighContrastMode();

	//	color (pForceColor) is determined externally, including DrawMode changes

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
	{
		Size aOnePixel = pDev->PixelToLogic(Size(1,1));
		long nOneX = aOnePixel.Width();
		nInitPosX += nMirrorW - nOneX;
	}
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	Rectangle aClipRect( Point(nScrX, nScrY), Size(nScrW, nScrH) );
	if (bMetaFile)
	{
		pDev->Push();
		pDev->IntersectClipRegion( aClipRect );
	}
	else
		pDev->SetClipRegion( Region( aClipRect ) );

    svx::frame::Array& rArray = mrTabInfo.maArray;

	long nPosY = nScrY;
	for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
	{
		//	Rotated wird auch 1 Zeile ueber/unter Changed gezeichnet, falls Teile
		//	in die Zeile hineinragen...

        RowInfo& rPrevRowInfo = pRowInfo[nArrY-1];
        RowInfo& rThisRowInfo = pRowInfo[nArrY];
        RowInfo& rNextRowInfo = pRowInfo[nArrY+1];

        size_t nRow = static_cast< size_t >( nArrY );

        long nRowHeight = rThisRowInfo.nHeight;
        if ( rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE &&
             ( rThisRowInfo.bChanged || rPrevRowInfo.bChanged ||
               ( nArrY+1<nArrCount && rNextRowInfo.bChanged ) ) )
		{
            SCROW nY = rThisRowInfo.nRowNo;
			long nPosX = 0;
			SCCOL nX;
			for (nX=0; nX<=nRotMax; nX++)
			{
				if (nX==nX1) nPosX = nInitPosX;		// calculated individually for preceding positions

                sal_uInt16 nArrX = nX + 1;

                CellInfo* pInfo = &rThisRowInfo.pCellInfo[nArrX];
                long nColWidth = pRowInfo[0].pCellInfo[nArrX].nWidth;
				if ( pInfo->nRotateDir > SC_ROTDIR_STANDARD &&
						!pInfo->bHOverlapped && !pInfo->bVOverlapped )
				{
					pPattern = pInfo->pPatternAttr;
					pCondSet = pInfo->pConditionSet;
					if (!pPattern)
					{
						pPattern = pDoc->GetPattern( nX, nY, nTab );
						pInfo->pPatternAttr = pPattern;
						pCondSet = pDoc->GetCondResult( nX, nY, nTab );
						pInfo->pConditionSet = pCondSet;
					}

					//!	LastPattern etc.

					long nAttrRotate = pPattern->GetRotateVal( pCondSet );
					SvxRotateMode eRotMode = (SvxRotateMode)((const SvxRotateModeItem&)
									pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet)).GetValue();

					if ( nAttrRotate )
					{
						if (nX<nX1)			// negative Position berechnen
						{
							nPosX = nInitPosX;
							SCCOL nCol = nX1;
							while (nCol > nX)
							{
								--nCol;
								nPosX -= nLayoutSign * (long) pRowInfo[0].pCellInfo[nCol+1].nWidth;
							}
						}

						//	Startposition minus 1, damit auch schraege Hintergruende
						//	zur Umrandung passen (Umrandung ist auf dem Gitter)

						long nTop = nPosY - 1;
						long nBottom = nPosY + nRowHeight - 1;
						long nTopLeft = nPosX - nLayoutSign;
						long nTopRight = nPosX + ( nColWidth - 1 ) * nLayoutSign;
						long nBotLeft = nTopLeft;
						long nBotRight = nTopRight;

						//	inclusion of the sign here hasn't been decided yet
						//	(if not, the extension of the non-rotated background must also be changed)
						double nRealOrient = nLayoutSign * nAttrRotate * F_PI18000;		// 1/100th degrees
						double nCos = cos( nRealOrient );
						double nSin = sin( nRealOrient );
						//!	begrenzen !!!
						long nSkew = (long) ( nRowHeight * nCos / nSin );

						switch (eRotMode)
						{
							case SVX_ROTATE_MODE_BOTTOM:
								nTopLeft += nSkew;
								nTopRight += nSkew;
								break;
							case SVX_ROTATE_MODE_CENTER:
								nSkew /= 2;
								nTopLeft += nSkew;
								nTopRight += nSkew;
								nBotLeft -= nSkew;
								nBotRight -= nSkew;
								break;
							case SVX_ROTATE_MODE_TOP:
								nBotLeft -= nSkew;
								nBotRight -= nSkew;
								break;
                            default:
                            {
                                // added to avoid warnings
                            }
						}

						Point aPoints[4];
						aPoints[0] = Point( nTopLeft, nTop );
						aPoints[1] = Point( nTopRight, nTop );
						aPoints[2] = Point( nBotRight, nBottom );
						aPoints[3] = Point( nBotLeft, nBottom );

						const SvxBrushItem* pBackground = pInfo->pBackground;
						if (!pBackground)
							pBackground = (const SvxBrushItem*) &pPattern->GetItem(
												ATTR_BACKGROUND, pCondSet );
						if (bCellContrast)
						{
							//	high contrast for cell borders and backgrounds -> empty background
							pBackground = ScGlobal::GetEmptyBrushItem();
						}
						const Color& rColor = pBackground->GetColor();
						if ( rColor.GetTransparency() != 255 )
						{
							//	#95879# draw background only for the changed row itself
							//	(background doesn't extend into other cells).
							//	For the borders (rotated and normal), clipping should be
							//	set if the row isn't changed, but at least the borders
							//	don't cover the cell contents.
                            if ( rThisRowInfo.bChanged )
							{
								Polygon aPoly( 4, aPoints );

								//	ohne Pen wird bei DrawPolygon rechts und unten
								//	ein Pixel weggelassen...
								if ( rColor.GetTransparency() == 0 )
									pDev->SetLineColor(rColor);
								else
									pDev->SetLineColor();
								pDev->SetFillColor(rColor);
								pDev->DrawPolygon( aPoly );
							}
						}

                        svx::frame::Style aTopLine, aBottomLine, aLeftLine, aRightLine;

						if ( nX < nX1 || nX > nX2 )		// Attribute in FillInfo nicht gesetzt
						{
							//!	Seitengrenzen fuer Druck beruecksichtigen !!!!!
                            const SvxBorderLine* pLeftLine;
                            const SvxBorderLine* pTopLine;
                            const SvxBorderLine* pRightLine;
                            const SvxBorderLine* pBottomLine;
							pDoc->GetBorderLines( nX, nY, nTab,
									&pLeftLine, &pTopLine, &pRightLine, &pBottomLine );
                            aTopLine.Set( pTopLine, nPPTY );
                            aBottomLine.Set( pBottomLine, nPPTY );
                            aLeftLine.Set( pLeftLine, nPPTX );
                            aRightLine.Set( pRightLine, nPPTX );
						}
                        else
                        {
                            size_t nCol = lclGetArrayColFromCellInfoX( nArrX, nX1, nX2, bLayoutRTL );
                            aTopLine = rArray.GetCellStyleTop( nCol, nRow );
                            aBottomLine = rArray.GetCellStyleBottom( nCol, nRow );
                            aLeftLine = rArray.GetCellStyleLeft( nCol, nRow );
                            aRightLine = rArray.GetCellStyleRight( nCol, nRow );
                            // in RTL mode the array is already mirrored -> swap back left/right borders
                            if( bLayoutRTL )
                                std::swap( aLeftLine, aRightLine );
                        }

                        lcl_HorizLine( *pDev, aPoints[bLayoutRTL?1:0], aPoints[bLayoutRTL?0:1], aTopLine, pForceColor );
                        lcl_HorizLine( *pDev, aPoints[bLayoutRTL?2:3], aPoints[bLayoutRTL?3:2], aBottomLine, pForceColor );

                        lcl_VertLine( *pDev, aPoints[0], aPoints[3], aLeftLine, aTopLine, aBottomLine, pForceColor );
                        lcl_VertLine( *pDev, aPoints[1], aPoints[2], aRightLine, aTopLine, aBottomLine, pForceColor );
					}
				}
				nPosX += nColWidth * nLayoutSign;
			}

			//	erst hinterher im zweiten Schritt die Linien fuer normale Ausgabe loeschen

			nX = nX1 > 0 ? (nX1-1) : static_cast<SCCOL>(0);
			for (; nX<=nX2+1; nX++)			// sichtbarer Teil +- 1
			{
                sal_uInt16 nArrX = nX + 1;
                CellInfo& rInfo = rThisRowInfo.pCellInfo[nArrX];
                if ( rInfo.nRotateDir > SC_ROTDIR_STANDARD &&
                        !rInfo.bHOverlapped && !rInfo.bVOverlapped )
				{
                    pPattern = rInfo.pPatternAttr;
                    pCondSet = rInfo.pConditionSet;
					SvxRotateMode eRotMode = (SvxRotateMode)((const SvxRotateModeItem&)
									pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet)).GetValue();

                    size_t nCol = lclGetArrayColFromCellInfoX( nArrX, nX1, nX2, bLayoutRTL );

					//	horizontal: angrenzende Linie verlaengern
					//	(nur, wenn die gedrehte Zelle eine Umrandung hat)
                    sal_uInt16 nDir = rInfo.nRotateDir;
                    if ( rArray.GetCellStyleTop( nCol, nRow ).Prim() && eRotMode != SVX_ROTATE_MODE_TOP )
                    {
                        svx::frame::Style aStyle( lcl_FindHorLine( pDoc, nX, nY, nTab, nDir, sal_True ), nPPTY );
                        rArray.SetCellStyleTop( nCol, nRow, aStyle );
                        if( nRow > 0 )
                            rArray.SetCellStyleBottom( nCol, nRow - 1, aStyle );
                    }
                    if ( rArray.GetCellStyleBottom( nCol, nRow ).Prim() && eRotMode != SVX_ROTATE_MODE_BOTTOM )
                    {
                        svx::frame::Style aStyle( lcl_FindHorLine( pDoc, nX, nY, nTab, nDir, sal_False ), nPPTY );
                        rArray.SetCellStyleBottom( nCol, nRow, aStyle );
                        if( nRow + 1 < rArray.GetRowCount() )
                            rArray.SetCellStyleTop( nCol, nRow + 1, aStyle );
                    }

                    // always remove vertical borders
                    if( !rArray.IsMergedOverlappedLeft( nCol, nRow ) )
                    {
                        rArray.SetCellStyleLeft( nCol, nRow, svx::frame::Style() );
                        if( nCol > 0 )
                            rArray.SetCellStyleRight( nCol - 1, nRow, svx::frame::Style() );
                    }
                    if( !rArray.IsMergedOverlappedRight( nCol, nRow ) )
                    {
                        rArray.SetCellStyleRight( nCol, nRow, svx::frame::Style() );
                        if( nCol + 1 < rArray.GetColCount() )
                            rArray.SetCellStyleLeft( nCol + 1, nRow, svx::frame::Style() );
                    }

                    // remove diagonal borders
                    rArray.SetCellStyleTLBR( nCol, nRow, svx::frame::Style() );
                    rArray.SetCellStyleBLTR( nCol, nRow, svx::frame::Style() );
				}
			}
		}
		nPosY += nRowHeight;
	}

	if (bMetaFile)
		pDev->Pop();
	else
		pDev->SetClipRegion();
}

//	Drucker

Region ScOutputData::GetChangedAreaRegion()
{
    Region aRegion;
    Rectangle aDrawingRect;
    bool bHad(false);
    long nPosY = nScrY;
    SCSIZE nArrY;

    aDrawingRect.Left() = nScrX;
    aDrawingRect.Right() = nScrX+nScrW-1;

    for(nArrY=1; nArrY+1<nArrCount; nArrY++)
    {
        RowInfo* pThisRowInfo = &pRowInfo[nArrY];

        if(pThisRowInfo->bChanged)
        {
            if(!bHad)
            {
                aDrawingRect.Top() = nPosY;
                bHad = true;
            }

            aDrawingRect.Bottom() = nPosY + pRowInfo[nArrY].nHeight - 1;
        }
        else if(bHad)
        {
            aRegion.Union(pDev->PixelToLogic(aDrawingRect));
            bHad = false;
        }

        nPosY += pRowInfo[nArrY].nHeight;
    }

    if(bHad)
    {
        aRegion.Union(pDev->PixelToLogic(aDrawingRect));
    }

    return aRegion;
}

sal_Bool ScOutputData::SetChangedClip()
{
	PolyPolygon aPoly;

	Rectangle aDrawingRect;
	aDrawingRect.Left() = nScrX;
	aDrawingRect.Right() = nScrX+nScrW-1;

	sal_Bool	bHad	= sal_False;
	long	nPosY	= nScrY;
	SCSIZE	nArrY;
	for (nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];

		if ( pThisRowInfo->bChanged )
		{
			if (!bHad)
			{
				aDrawingRect.Top() = nPosY;
				bHad = sal_True;
			}
			aDrawingRect.Bottom() = nPosY + pRowInfo[nArrY].nHeight - 1;
		}
		else if (bHad)
		{
			aPoly.Insert( Polygon( pDev->PixelToLogic(aDrawingRect) ) );
			bHad = sal_False;
		}
		nPosY += pRowInfo[nArrY].nHeight;
	}

	if (bHad)
		aPoly.Insert( Polygon( pDev->PixelToLogic(aDrawingRect) ) );

	sal_Bool bRet = (aPoly.Count() != 0);
	if (bRet)
		pDev->SetClipRegion(Region(aPoly));
	return bRet;
}

void ScOutputData::FindChanged()
{
	SCCOL	nX;
	SCSIZE	nArrY;

	sal_Bool bWasIdleDisabled = pDoc->IsIdleDisabled();
	pDoc->DisableIdle( sal_True );
	for (nArrY=0; nArrY<nArrCount; nArrY++)
		pRowInfo[nArrY].bChanged = sal_False;

	sal_Bool bProgress = sal_False;
	for (nArrY=0; nArrY<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		for (nX=nX1; nX<=nX2; nX++)
		{
			ScBaseCell* pCell = pThisRowInfo->pCellInfo[nX+1].pCell;
			if (pCell)
				if (pCell->GetCellType() == CELLTYPE_FORMULA)
				{
					ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
					if ( !bProgress && pFCell->GetDirty() )
					{
						ScProgress::CreateInterpretProgress( pDoc, sal_True );
						bProgress = sal_True;
					}
					if (!pFCell->IsRunning())
					{
                        (void)pFCell->GetValue();
						if (pFCell->IsChanged())
						{
							pThisRowInfo->bChanged = sal_True;
							if ( pThisRowInfo->pCellInfo[nX+1].bMerged )
							{
								SCSIZE nOverY = nArrY + 1;
								while ( nOverY<nArrCount &&
										pRowInfo[nOverY].pCellInfo[nX+1].bVOverlapped )
								{
									pRowInfo[nOverY].bChanged = sal_True;
									++nOverY;
								}
							}
						}
					}
				}
		}
	}
	if ( bProgress )
		ScProgress::DeleteInterpretProgress();
	pDoc->DisableIdle( bWasIdleDisabled );
}

#ifdef OLD_SELECTION_PAINT
void ScOutputData::DrawMark( Window* pWin )
{
    Rectangle aRect;
    ScInvertMerger aInvert( pWin );
    //!	additional method AddLineRect for ScInvertMerger?

    long nPosY = nScrY;
    for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
    {
        RowInfo* pThisRowInfo = &pRowInfo[nArrY];
        if (pThisRowInfo->bChanged)
        {
            long nPosX = nScrX;
            if (bLayoutRTL)
                nPosX += nMirrorW - 1;      // always in pixels

            aRect = Rectangle( Point( nPosX,nPosY ), Size(1, pThisRowInfo->nHeight) );
            if (bLayoutRTL)
                aRect.Left() = aRect.Right() + 1;
            else
                aRect.Right() = aRect.Left() - 1;

            sal_Bool bOldMarked = sal_False;
            for (SCCOL nX=nX1; nX<=nX2; nX++)
            {
                if (pThisRowInfo->pCellInfo[nX+1].bMarked != bOldMarked)
                {
                    if (bOldMarked && aRect.Right() >= aRect.Left())
                        aInvert.AddRect( aRect );

                    if (bLayoutRTL)
                        aRect.Right() = nPosX;
                    else
                        aRect.Left() = nPosX;

                    bOldMarked = pThisRowInfo->pCellInfo[nX+1].bMarked;
                }

                if (bLayoutRTL)
                {
                    nPosX -= pRowInfo[0].pCellInfo[nX+1].nWidth;
                    aRect.Left() = nPosX+1;
                }
                else
                {
                    nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth;
                    aRect.Right() = nPosX-1;
                }
            }
            if (bOldMarked && aRect.Right() >= aRect.Left())
                aInvert.AddRect( aRect );
        }
        nPosY += pThisRowInfo->nHeight;
    }
}
#endif

void ScOutputData::DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
								SCCOL nRefEndX, SCROW nRefEndY,
								const Color& rColor, sal_Bool bHandle )
{
	PutInOrder( nRefStartX, nRefEndX );
	PutInOrder( nRefStartY, nRefEndY );

	if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
		pDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );

	if ( nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
		 nRefStartY <= nVisY2 && nRefEndY >= nVisY1 )
	{
		long nMinX = nScrX;
		long nMinY = nScrY;
		long nMaxX = nScrX+nScrW-1;
		long nMaxY = nScrY+nScrH-1;
		if ( bLayoutRTL )
		{
			long nTemp = nMinX;
			nMinX = nMaxX;
			nMaxX = nTemp;
		}
		long nLayoutSign = bLayoutRTL ? -1 : 1;

		sal_Bool bTop    = sal_False;
		sal_Bool bBottom = sal_False;
		sal_Bool bLeft   = sal_False;
		sal_Bool bRight	 = sal_False;

		long nPosY = nScrY;
		sal_Bool bNoStartY = ( nY1 < nRefStartY );
		sal_Bool bNoEndY   = sal_False;
		for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)		// loop to end for bNoEndY check
		{
			SCROW nY = pRowInfo[nArrY].nRowNo;

			if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
			{
				nMinY = nPosY;
				bTop = sal_True;
			}
			if ( nY==nRefEndY )
			{
				nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
				bBottom = sal_True;
			}
			if ( nY>nRefEndY && bNoEndY )
			{
				nMaxY = nPosY-2;
				bBottom = sal_True;
			}
			bNoStartY = ( nY < nRefStartY );
			bNoEndY   = ( nY < nRefEndY );
			nPosY += pRowInfo[nArrY].nHeight;
		}

		long nPosX = nScrX;
		if ( bLayoutRTL )
			nPosX += nMirrorW - 1;		// always in pixels

		for (SCCOL nX=nX1; nX<=nX2; nX++)
		{
			if ( nX==nRefStartX )
			{
				nMinX = nPosX;
				bLeft = sal_True;
			}
			if ( nX==nRefEndX )
			{
				nMaxX = nPosX + ( pRowInfo[0].pCellInfo[nX+1].nWidth - 2 ) * nLayoutSign;
				bRight = sal_True;
			}
			nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
		}

		if ( nMaxX * nLayoutSign >= nMinX * nLayoutSign &&
			 nMaxY >= nMinY )
		{
			pDev->SetLineColor( rColor );
			if (bTop && bBottom && bLeft && bRight)
			{
				pDev->SetFillColor();
				pDev->DrawRect( Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
			}
			else
			{
				if (bTop)
					pDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
				if (bBottom)
					pDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
				if (bLeft)
					pDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
				if (bRight)
					pDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
			}
			if ( bHandle && bRight && bBottom )
			{
				pDev->SetLineColor();
				pDev->SetFillColor( rColor );
				pDev->DrawRect( Rectangle( nMaxX-3*nLayoutSign, nMaxY-3, nMaxX+nLayoutSign, nMaxY+1 ) );
			}
		}
	}
}

void ScOutputData::DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
								SCCOL nRefEndX, SCROW nRefEndY,
								const Color& rColor, sal_uInt16 nType )
{
	PutInOrder( nRefStartX, nRefEndX );
	PutInOrder( nRefStartY, nRefEndY );

	if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
		pDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );

	if ( nRefStartX <= nVisX2 + 1 && nRefEndX >= nVisX1 &&
		 nRefStartY <= nVisY2 + 1 && nRefEndY >= nVisY1 )		// +1 because it touches next cells left/top
	{
		long nMinX = nScrX;
		long nMinY = nScrY;
		long nMaxX = nScrX+nScrW-1;
		long nMaxY = nScrY+nScrH-1;
		if ( bLayoutRTL )
		{
			long nTemp = nMinX;
			nMinX = nMaxX;
			nMaxX = nTemp;
		}
		long nLayoutSign = bLayoutRTL ? -1 : 1;

		sal_Bool bTop    = sal_False;
		sal_Bool bBottom = sal_False;
		sal_Bool bLeft   = sal_False;
		sal_Bool bRight	 = sal_False;

		long nPosY = nScrY;
		sal_Bool bNoStartY = ( nY1 < nRefStartY );
		sal_Bool bNoEndY   = sal_False;
		for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)		// loop to end for bNoEndY check
		{
			SCROW nY = pRowInfo[nArrY].nRowNo;

			if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
			{
				nMinY = nPosY - 1;
				bTop = sal_True;
			}
			if ( nY==nRefEndY )
			{
				nMaxY = nPosY + pRowInfo[nArrY].nHeight - 1;
				bBottom = sal_True;
			}
			if ( nY>nRefEndY && bNoEndY )
			{
				nMaxY = nPosY - 1;
				bBottom = sal_True;
			}
			bNoStartY = ( nY < nRefStartY );
			bNoEndY   = ( nY < nRefEndY );
			nPosY += pRowInfo[nArrY].nHeight;
		}

		long nPosX = nScrX;
		if ( bLayoutRTL )
			nPosX += nMirrorW - 1;		// always in pixels

		for (SCCOL nX=nX1; nX<=nX2+1; nX++)
		{
			if ( nX==nRefStartX )
			{
				nMinX = nPosX - nLayoutSign;
				bLeft = sal_True;
			}
			if ( nX==nRefEndX )
			{
				nMaxX = nPosX + ( pRowInfo[0].pCellInfo[nX+1].nWidth - 1 ) * nLayoutSign;
				bRight = sal_True;
			}
			nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
		}

		if ( nMaxX * nLayoutSign >= nMinX * nLayoutSign &&
			 nMaxY >= nMinY )
		{
			if ( nType == SC_CAT_DELETE_ROWS )
				bLeft = bRight = bBottom = sal_False;		//! dicke Linie ???
			else if ( nType == SC_CAT_DELETE_COLS )
				bTop = bBottom = bRight = sal_False;		//! dicke Linie ???

			pDev->SetLineColor( rColor );
			if (bTop && bBottom && bLeft && bRight)
			{
				pDev->SetFillColor();
				pDev->DrawRect( Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
			}
			else
			{
				if (bTop)
				{
					pDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
					if ( nType == SC_CAT_DELETE_ROWS )
						pDev->DrawLine( Point( nMinX,nMinY+1 ), Point( nMaxX,nMinY+1 ) );
				}
				if (bBottom)
					pDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
				if (bLeft)
				{
					pDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
					if ( nType == SC_CAT_DELETE_COLS )
						pDev->DrawLine( Point( nMinX+nLayoutSign,nMinY ), Point( nMinX+nLayoutSign,nMaxY ) );
				}
				if (bRight)
					pDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
			}
			if ( bLeft && bTop )
			{
				pDev->SetLineColor();
				pDev->SetFillColor( rColor );
				pDev->DrawRect( Rectangle( nMinX+nLayoutSign, nMinY+1, nMinX+3*nLayoutSign, nMinY+3 ) );
			}
		}
	}
}

void ScOutputData::DrawChangeTrack()
{
	ScChangeTrack* pTrack = pDoc->GetChangeTrack();
	ScChangeViewSettings* pSettings = pDoc->GetChangeViewSettings();
	if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
		return;			// nix da oder abgeschaltet

	ScActionColorChanger aColorChanger(*pTrack);

	//	Clipping passiert von aussen
	//!	ohne Clipping, nur betroffene Zeilen painten ??!??!?

	SCCOL nEndX = nX2;
	SCROW nEndY = nY2;
	if ( nEndX < MAXCOL ) ++nEndX;		// auch noch von der naechsten Zelle, weil die Markierung
	if ( nEndY < MAXROW ) ++nEndY;		// in die jeweils vorhergehende Zelle hineinragt
	ScRange aViewRange( nX1, nY1, nTab, nEndX, nEndY, nTab );
	const ScChangeAction* pAction = pTrack->GetFirst();
	while (pAction)
	{
		ScChangeActionType eActionType;
		if ( pAction->IsVisible() )
		{
			eActionType = pAction->GetType();
			const ScBigRange& rBig = pAction->GetBigRange();
			if ( rBig.aStart.Tab() == nTab )
			{
				ScRange aRange = rBig.MakeRange();

				if ( eActionType == SC_CAT_DELETE_ROWS )
					aRange.aEnd.SetRow( aRange.aStart.Row() );
				else if ( eActionType == SC_CAT_DELETE_COLS )
					aRange.aEnd.SetCol( aRange.aStart.Col() );

				if ( aRange.Intersects( aViewRange ) &&
					 ScViewUtil::IsActionShown( *pAction, *pSettings, *pDoc ) )
				{
					aColorChanger.Update( *pAction );
					Color aColor( aColorChanger.GetColor() );
					DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
                                    aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );

				}
			}
			if ( eActionType == SC_CAT_MOVE &&
					((const ScChangeActionMove*)pAction)->
						GetFromRange().aStart.Tab() == nTab )
			{
				ScRange aRange = ((const ScChangeActionMove*)pAction)->
						GetFromRange().MakeRange();
				if ( aRange.Intersects( aViewRange ) &&
					 ScViewUtil::IsActionShown( *pAction, *pSettings, *pDoc ) )
				{
					aColorChanger.Update( *pAction );
					Color aColor( aColorChanger.GetColor() );
					DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
                                    aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
				}
			}
		}

		pAction = pAction->GetNext();
	}
}

void ScOutputData::DrawNoteMarks()
{
	sal_Bool bFirst = sal_True;

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
		nInitPosX += nMirrorW - 1;				// always in pixels
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	long nPosY = nScrY;
	for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		if ( pThisRowInfo->bChanged )
		{
			long nPosX = nInitPosX;
			for (SCCOL nX=nX1; nX<=nX2; nX++)
			{
				CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];
				ScBaseCell* pCell = pInfo->pCell;
				sal_Bool bIsMerged = sal_False;

				if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
				{
					// find start of merged cell
					bIsMerged = sal_True;
					SCROW nY = pRowInfo[nArrY].nRowNo;
					SCCOL nMergeX = nX;
					SCROW nMergeY = nY;
					pDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
					pCell = pDoc->GetCell( ScAddress(nMergeX,nMergeY,nTab) );
					// use origin's pCell for NotePtr test below
				}

                if ( pCell && pCell->HasNote() && ( bIsMerged ||
						( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
				{
					if (bFirst)
					{
						pDev->SetLineColor();

						const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
						if ( bUseStyleColor && rStyleSettings.GetHighContrastMode() )
                            pDev->SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
						else
							pDev->SetFillColor(COL_LIGHTRED);

						bFirst = sal_False;
					}

					long nMarkX = nPosX + ( pRowInfo[0].pCellInfo[nX+1].nWidth - 4 ) * nLayoutSign;
					if ( bIsMerged || pInfo->bMerged )
					{
						//	if merged, add widths of all cells
						SCCOL nNextX = nX + 1;
						while ( nNextX <= nX2 + 1 && pThisRowInfo->pCellInfo[nNextX+1].bHOverlapped )
						{
							nMarkX += pRowInfo[0].pCellInfo[nNextX+1].nWidth * nLayoutSign;
							++nNextX;
						}
					}
					if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
						pDev->DrawRect( Rectangle( nMarkX,nPosY,nMarkX+2*nLayoutSign,nPosY+2 ) );
				}

				nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
			}
		}
		nPosY += pThisRowInfo->nHeight;
	}
}

void ScOutputData::AddPDFNotes()
{
    vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() );
    if ( !pPDFData || !pPDFData->GetIsExportNotes() )
        return;

    long nInitPosX = nScrX;
    if ( bLayoutRTL )
    {
        Size aOnePixel = pDev->PixelToLogic(Size(1,1));
        long nOneX = aOnePixel.Width();
        nInitPosX += nMirrorW - nOneX;
    }
    long nLayoutSign = bLayoutRTL ? -1 : 1;

    long nPosY = nScrY;
    for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
    {
        RowInfo* pThisRowInfo = &pRowInfo[nArrY];
        if ( pThisRowInfo->bChanged )
        {
            long nPosX = nInitPosX;
            for (SCCOL nX=nX1; nX<=nX2; nX++)
            {
                CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];
                ScBaseCell* pCell = pInfo->pCell;
                sal_Bool bIsMerged = sal_False;
                SCROW nY = pRowInfo[nArrY].nRowNo;
                SCCOL nMergeX = nX;
                SCROW nMergeY = nY;

                if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
                {
                    // find start of merged cell
                    bIsMerged = sal_True;
                    pDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
                    pCell = pDoc->GetCell( ScAddress(nMergeX,nMergeY,nTab) );
                    // use origin's pCell for NotePtr test below
                }

                if ( pCell && pCell->HasNote() && ( bIsMerged ||
                        ( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
                {
                    long nNoteWidth = (long)( SC_CLIPMARK_SIZE * nPPTX );
                    long nNoteHeight = (long)( SC_CLIPMARK_SIZE * nPPTY );

                    long nMarkX = nPosX + ( pRowInfo[0].pCellInfo[nX+1].nWidth - nNoteWidth ) * nLayoutSign;
                    if ( bIsMerged || pInfo->bMerged )
                    {
                        //  if merged, add widths of all cells
                        SCCOL nNextX = nX + 1;
                        while ( nNextX <= nX2 + 1 && pThisRowInfo->pCellInfo[nNextX+1].bHOverlapped )
                        {
                            nMarkX += pRowInfo[0].pCellInfo[nNextX+1].nWidth * nLayoutSign;
                            ++nNextX;
                        }
                    }
                    if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
                    {
                        Rectangle aNoteRect( nMarkX, nPosY, nMarkX+nNoteWidth*nLayoutSign, nPosY+nNoteHeight );
                        const ScPostIt* pNote = pCell->GetNote();

                        // Note title is the cell address (as on printed note pages)
                        String aTitle;
                        ScAddress aAddress( nMergeX, nMergeY, nTab );
                        aAddress.Format( aTitle, SCA_VALID, pDoc, pDoc->GetAddressConvention() );

                        // Content has to be a simple string without line breaks
                        String aContent = pNote->GetText();
                        xub_StrLen nPos;
                        while ( (nPos=aContent.Search('\n')) != STRING_NOTFOUND )
                            aContent.SetChar( nPos, ' ' );

                        vcl::PDFNote aNote;
                        aNote.Title = aTitle;
                        aNote.Contents = aContent;
                        pPDFData->CreateNote( aNoteRect, aNote );
                    }
                }

                nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
            }
        }
        nPosY += pThisRowInfo->nHeight;
    }
}

void ScOutputData::DrawClipMarks()
{
	if (!bAnyClipped)
		return;

	Color aArrowFillCol( COL_LIGHTRED );

	sal_uLong nOldDrawMode = pDev->GetDrawMode();
	const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
	if ( bUseStyleColor && rStyleSettings.GetHighContrastMode() )
	{
		//	use DrawMode to change the arrow's outline color
		pDev->SetDrawMode( nOldDrawMode | DRAWMODE_SETTINGSLINE );
		//	use text color also for the fill color
        aArrowFillCol.SetColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
	}

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
		nInitPosX += nMirrorW - 1;				// always in pixels
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	Rectangle aCellRect;
	long nPosY = nScrY;
	for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		if ( pThisRowInfo->bChanged )
		{
			SCROW nY = pThisRowInfo->nRowNo;
			long nPosX = nInitPosX;
			for (SCCOL nX=nX1; nX<=nX2; nX++)
			{
				CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];
				if (pInfo->nClipMark)
				{
					if (pInfo->bHOverlapped || pInfo->bVOverlapped)
					{
						//	merge origin may be outside of visible area - use document functions

						SCCOL nOverX = nX;
						SCROW nOverY = nY;
						long nStartPosX = nPosX;
						long nStartPosY = nPosY;

						while ( nOverX > 0 && ( ((const ScMergeFlagAttr*)pDoc->GetAttr(
								nOverX, nOverY, nTab, ATTR_MERGE_FLAG ))->GetValue() & SC_MF_HOR ) )
						{
							--nOverX;
							nStartPosX -= nLayoutSign * (long) ( pDoc->GetColWidth(nOverX,nTab) * nPPTX );
						}

						while ( nOverY > 0 && ( ((const ScMergeFlagAttr*)pDoc->GetAttr(
								nOverX, nOverY, nTab, ATTR_MERGE_FLAG ))->GetValue() & SC_MF_VER ) )
						{
							--nOverY;
							nStartPosY -= nLayoutSign * (long) ( pDoc->GetRowHeight(nOverY,nTab) * nPPTY );
						}

						long nOutWidth = (long) ( pDoc->GetColWidth(nOverX,nTab) * nPPTX );
						long nOutHeight = (long) ( pDoc->GetRowHeight(nOverY,nTab) * nPPTY );

						const ScMergeAttr* pMerge = (const ScMergeAttr*)
									pDoc->GetAttr( nOverX, nOverY, nTab, ATTR_MERGE );
						SCCOL nCountX = pMerge->GetColMerge();
						for (SCCOL i=1; i<nCountX; i++)
							nOutWidth += (long) ( pDoc->GetColWidth(nOverX+i,nTab) * nPPTX );
						SCROW nCountY = pMerge->GetRowMerge();
                        nOutHeight += (long) pDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, nPPTY);

						if ( bLayoutRTL )
							nStartPosX -= nOutWidth - 1;
						aCellRect = Rectangle( Point( nStartPosX, nStartPosY ), Size( nOutWidth, nOutHeight ) );
					}
					else
					{
						long nOutWidth = pRowInfo[0].pCellInfo[nX+1].nWidth;
						long nOutHeight = pThisRowInfo->nHeight;

						if ( pInfo->bMerged && pInfo->pPatternAttr )
						{
							SCCOL nOverX = nX;
							SCROW nOverY = nY;
							const ScMergeAttr* pMerge =
									(ScMergeAttr*)&pInfo->pPatternAttr->GetItem(ATTR_MERGE);
							SCCOL nCountX = pMerge->GetColMerge();
							for (SCCOL i=1; i<nCountX; i++)
								nOutWidth += (long) ( pDoc->GetColWidth(nOverX+i,nTab) * nPPTX );
							SCROW nCountY = pMerge->GetRowMerge();
                            nOutHeight += (long) pDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, nPPTY);
						}

						long nStartPosX = nPosX;
						if ( bLayoutRTL )
							nStartPosX -= nOutWidth - 1;
                        // #i80447# create aCellRect from two points in case nOutWidth is 0
                        aCellRect = Rectangle( Point( nStartPosX, nPosY ),
                                               Point( nStartPosX+nOutWidth-1, nPosY+nOutHeight-1 ) );
					}

					aCellRect.Bottom() -= 1;	// don't paint over the cell grid
					if ( bLayoutRTL )
						aCellRect.Left() += 1;
					else
						aCellRect.Right() -= 1;

					long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX );
					Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );

					if ( pInfo->nClipMark & ( bLayoutRTL ? SC_CLIPMARK_RIGHT : SC_CLIPMARK_LEFT ) )
					{
						//	visually left
						Rectangle aMarkRect = aCellRect;
						aMarkRect.Right() = aCellRect.Left()+nMarkPixel-1;
#if 0
						//!	Test
						pDev->SetLineColor(); pDev->SetFillColor(COL_YELLOW);
						pDev->DrawRect(aMarkRect);
						//!	Test
#endif
						SvxFont::DrawArrow( *pDev, aMarkRect, aMarkSize, aArrowFillCol, sal_True );
					}
					if ( pInfo->nClipMark & ( bLayoutRTL ? SC_CLIPMARK_LEFT : SC_CLIPMARK_RIGHT ) )
					{
						//	visually right
						Rectangle aMarkRect = aCellRect;
						aMarkRect.Left() = aCellRect.Right()-nMarkPixel+1;
#if 0
						//!	Test
						pDev->SetLineColor(); pDev->SetFillColor(COL_LIGHTGREEN);
						pDev->DrawRect(aMarkRect);
						//!	Test
#endif
						SvxFont::DrawArrow( *pDev, aMarkRect, aMarkSize, aArrowFillCol, sal_False );
					}
				}
				nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
			}
		}
		nPosY += pThisRowInfo->nHeight;
	}

	pDev->SetDrawMode(nOldDrawMode);
}



