diff options
Diffstat (limited to 'sc/source/ui/view/output2.cxx')
-rw-r--r-- | sc/source/ui/view/output2.cxx | 3705 |
1 files changed, 3705 insertions, 0 deletions
diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx new file mode 100644 index 000000000000..b0df298315bf --- /dev/null +++ b/sc/source/ui/view/output2.cxx @@ -0,0 +1,3705 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <editeng/eeitem.hxx> + + +#include <editeng/adjitem.hxx> +#include <svx/algitem.hxx> +#include <editeng/brshitem.hxx> +#include <svtools/colorcfg.hxx> +#include <editeng/colritem.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/forbiddencharacterstable.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/langitem.hxx> +#include <svx/rotmodit.hxx> +#include <editeng/scripttypeitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/unolingu.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <vcl/svapp.hxx> +#include <vcl/metric.hxx> +#include <vcl/outdev.hxx> +#include <vcl/pdfextoutdevdata.hxx> + +#ifndef _SVSTDARR_USHORTS +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> +#endif + +#include "output.hxx" +#include "document.hxx" +#include "cell.hxx" +#include "attrib.hxx" +#include "patattr.hxx" +#include "cellform.hxx" +#include "editutil.hxx" +#include "progress.hxx" +#include "scmod.hxx" +#include "fillinfo.hxx" + +#include <boost/ptr_container/ptr_vector.hpp> + +#include <math.h> + +//! Autofilter-Breite mit column.cxx zusammenfassen +#define DROPDOWN_BITMAP_SIZE 18 + +#define DRAWTEXT_MAX 32767 + +const USHORT SC_SHRINKAGAIN_MAX = 7; + +// STATIC DATA ----------------------------------------------------------- + + +// ----------------------------------------------------------------------- + +class ScDrawStringsVars +{ + ScOutputData* pOutput; // Verbindung + + const ScPatternAttr* pPattern; // Attribute + const SfxItemSet* pCondSet; // aus bedingter Formatierung + + Font aFont; // aus Attributen erzeugt + FontMetric aMetric; + long nAscentPixel; // always pixels + SvxCellOrientation eAttrOrient; + SvxCellHorJustify eAttrHorJust; + SvxCellVerJustify eAttrVerJust; + const SvxMarginItem* pMargin; + USHORT nIndent; + BOOL bRotated; + + String aString; // Inhalte + Size aTextSize; + long nOriginalWidth; + long nMaxDigitWidth; + long nSignWidth; + long nDotWidth; + long nExpWidth; + + ScBaseCell* pLastCell; + ULONG nValueFormat; + BOOL bLineBreak; + BOOL bRepeat; + BOOL bShrink; + + BOOL bPixelToLogic; + BOOL bCellContrast; + + Color aBackConfigColor; // used for ScPatternAttr::GetFont calls + Color aTextConfigColor; + +public: + ScDrawStringsVars(ScOutputData* pData, BOOL bPTL); + ~ScDrawStringsVars(); + + // SetPattern = ex-SetVars + // SetPatternSimple: ohne Font + + void SetPattern( const ScPatternAttr* pNew, const SfxItemSet* pSet, ScBaseCell* pCell, BYTE nScript ); + void SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet ); + + BOOL SetText( ScBaseCell* pCell ); // TRUE -> pOldPattern vergessen + void SetTextToWidthOrHash( ScBaseCell* pCell, long nWidth ); + void SetAutoText( const String& rAutoText ); + + const ScPatternAttr* GetPattern() const { return pPattern; } + SvxCellOrientation GetOrient() const { return eAttrOrient; } + SvxCellHorJustify GetHorJust() const { return eAttrHorJust; } + SvxCellVerJustify GetVerJust() const { return eAttrVerJust; } + const SvxMarginItem* GetMargin() const { return pMargin; } + + USHORT GetLeftTotal() const { return pMargin->GetLeftMargin() + nIndent; } + + const String& GetString() const { return aString; } + const Size& GetTextSize() const { return aTextSize; } + long GetOriginalWidth() const { return nOriginalWidth; } + + ULONG GetValueFormat() const { return nValueFormat; } + BOOL GetLineBreak() const { return bLineBreak; } + BOOL IsRepeat() const { return bRepeat; } + BOOL IsShrink() const { return bShrink; } + + long GetAscent() const { return nAscentPixel; } + BOOL IsRotated() const { return bRotated; } + + void SetShrinkScale( long nScale, BYTE nScript ); + + BOOL HasCondHeight() const { return pCondSet && SFX_ITEM_SET == + pCondSet->GetItemState( ATTR_FONT_HEIGHT, TRUE ); } + + BOOL HasEditCharacters() const; + +private: + void SetHashText(); + long GetMaxDigitWidth(); // in logic units + long GetSignWidth(); + long GetDotWidth(); + long GetExpWidth(); + void TextChanged(); +}; + +//================================================================== + +ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, BOOL bPTL) : + pOutput ( pData ), + pPattern ( NULL ), + pCondSet ( NULL ), + eAttrOrient ( SVX_ORIENTATION_STANDARD ), + eAttrHorJust( SVX_HOR_JUSTIFY_STANDARD ), + eAttrVerJust( SVX_VER_JUSTIFY_BOTTOM ), + pMargin ( NULL ), + nIndent ( 0 ), + bRotated ( FALSE ), + nOriginalWidth( 0 ), + nMaxDigitWidth( 0 ), + nSignWidth( 0 ), + nDotWidth( 0 ), + nExpWidth( 0 ), + pLastCell ( NULL ), + nValueFormat( 0 ), + bLineBreak ( FALSE ), + bRepeat ( FALSE ), + bShrink ( FALSE ), + bPixelToLogic( bPTL ) +{ + ScModule* pScMod = SC_MOD(); + // #105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed TRUE) + bCellContrast = pOutput->bUseStyleColor && + Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + + const svtools::ColorConfig& rColorConfig = pScMod->GetColorConfig(); + aBackConfigColor.SetColor( rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor ); + aTextConfigColor.SetColor( rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor ); +} + +ScDrawStringsVars::~ScDrawStringsVars() +{ +} + +void ScDrawStringsVars::SetShrinkScale( long nScale, BYTE nScript ) +{ + // text remains valid, size is updated + + OutputDevice* pDev = pOutput->pDev; + OutputDevice* pRefDevice = pOutput->pRefDevice; + OutputDevice* pFmtDevice = pOutput->pFmtDevice; + + // call GetFont with a modified fraction, use only the height + + Fraction aFraction( nScale, 100 ); + if ( !bPixelToLogic ) + aFraction *= pOutput->aZoomY; + Font aTmpFont; + pPattern->GetFont( aTmpFont, SC_AUTOCOL_RAW, pFmtDevice, &aFraction, pCondSet, nScript ); + long nNewHeight = aTmpFont.GetHeight(); + if ( nNewHeight > 0 ) + aFont.SetHeight( nNewHeight ); + + // set font and dependent variables as in SetPattern + + pDev->SetFont( aFont ); + if ( pFmtDevice != pDev ) + pFmtDevice->SetFont( aFont ); + + aMetric = pFmtDevice->GetFontMetric(); + if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetIntLeading() == 0 ) + { + OutputDevice* pDefaultDev = Application::GetDefaultDevice(); + MapMode aOld = pDefaultDev->GetMapMode(); + pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() ); + aMetric = pDefaultDev->GetFontMetric( aFont ); + pDefaultDev->SetMapMode( aOld ); + } + + nAscentPixel = aMetric.GetAscent(); + if ( bPixelToLogic ) + nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height(); + + SetAutoText( aString ); // same text again, to get text size +} + +void ScDrawStringsVars::SetPattern( const ScPatternAttr* pNew, const SfxItemSet* pSet, + ScBaseCell* pCell, BYTE nScript ) +{ + nMaxDigitWidth = 0; + nSignWidth = 0; + nDotWidth = 0; + nExpWidth = 0; + + pPattern = pNew; + pCondSet = pSet; + + // pPattern auswerten + + OutputDevice* pDev = pOutput->pDev; + OutputDevice* pRefDevice = pOutput->pRefDevice; + OutputDevice* pFmtDevice = pOutput->pFmtDevice; + + // Font + + ScAutoFontColorMode eColorMode; + if ( pOutput->bUseStyleColor ) + { + if ( pOutput->bForceAutoColor ) + eColorMode = bCellContrast ? SC_AUTOCOL_IGNOREALL : SC_AUTOCOL_IGNOREFONT; + else + eColorMode = bCellContrast ? SC_AUTOCOL_IGNOREBACK : SC_AUTOCOL_DISPLAY; + } + else + eColorMode = SC_AUTOCOL_PRINT; + + if ( bPixelToLogic ) + pPattern->GetFont( aFont, eColorMode, pFmtDevice, NULL, pCondSet, nScript, + &aBackConfigColor, &aTextConfigColor ); + else + pPattern->GetFont( aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript, + &aBackConfigColor, &aTextConfigColor ); + aFont.SetAlign(ALIGN_BASELINE); + + // Orientierung + + eAttrOrient = pPattern->GetCellOrientation( pCondSet ); + + // alignment + + eAttrHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet )).GetValue(); + + eAttrVerJust = (SvxCellVerJustify)((const SvxVerJustifyItem&)pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet )).GetValue(); + if ( eAttrVerJust == SVX_VER_JUSTIFY_STANDARD ) + eAttrVerJust = SVX_VER_JUSTIFY_BOTTOM; + + // line break + + bLineBreak = ((const SfxBoolItem&)pPattern->GetItem( ATTR_LINEBREAK, pCondSet )).GetValue(); + + // handle "repeat" alignment + + bRepeat = ( eAttrHorJust == SVX_HOR_JUSTIFY_REPEAT ); + if ( bRepeat ) + { + // "repeat" disables rotation (before constructing the font) + eAttrOrient = SVX_ORIENTATION_STANDARD; + + // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled) + if ( bLineBreak ) + eAttrHorJust = SVX_HOR_JUSTIFY_STANDARD; + } + + short nRot; + switch (eAttrOrient) + { + case SVX_ORIENTATION_STANDARD: + nRot = 0; + bRotated = (((const SfxInt32Item&)pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet )).GetValue() != 0) && + !bRepeat; + break; + case SVX_ORIENTATION_STACKED: + nRot = 0; + bRotated = FALSE; + break; + case SVX_ORIENTATION_TOPBOTTOM: + nRot = 2700; + bRotated = FALSE; + break; + case SVX_ORIENTATION_BOTTOMTOP: + nRot = 900; + bRotated = FALSE; + break; + default: + DBG_ERROR("Falscher SvxCellOrientation Wert"); + nRot = 0; + bRotated = FALSE; + break; + } + aFont.SetOrientation( nRot ); + + // Syntax-Modus + + if (pOutput->bSyntaxMode) + pOutput->SetSyntaxColor( &aFont, pCell ); + + pDev->SetFont( aFont ); + if ( pFmtDevice != pDev ) + pFmtDevice->SetFont( aFont ); + + aMetric = pFmtDevice->GetFontMetric(); + + // + // Wenn auf dem Drucker das Leading 0 ist, gibt es Probleme + // -> Metric vom Bildschirm nehmen (wie EditEngine!) + // + + if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetIntLeading() == 0 ) + { + OutputDevice* pDefaultDev = Application::GetDefaultDevice(); + MapMode aOld = pDefaultDev->GetMapMode(); + pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() ); + aMetric = pDefaultDev->GetFontMetric( aFont ); + pDefaultDev->SetMapMode( aOld ); + } + + nAscentPixel = aMetric.GetAscent(); + if ( bPixelToLogic ) + nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height(); + + Color aULineColor( ((const SvxUnderlineItem&)pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet )).GetColor() ); + pDev->SetTextLineColor( aULineColor ); + + Color aOLineColor( ((const SvxOverlineItem&)pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet )).GetColor() ); + pDev->SetOverlineColor( aOLineColor ); + + // Zahlenformat + +// ULONG nOld = nValueFormat; + nValueFormat = pPattern->GetNumberFormat( pOutput->pDoc->GetFormatTable(), pCondSet ); + +/* s.u. + if (nValueFormat != nOld) + pLastCell = NULL; // immer neu formatieren +*/ + // Raender + + pMargin = (const SvxMarginItem*)&pPattern->GetItem( ATTR_MARGIN, pCondSet ); + if ( eAttrHorJust == SVX_HOR_JUSTIFY_LEFT ) + nIndent = ((const SfxUInt16Item&)pPattern->GetItem( ATTR_INDENT, pCondSet )).GetValue(); + else + nIndent = 0; + + // "Shrink to fit" + + bShrink = static_cast<const SfxBoolItem&>(pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue(); + + // zumindest die Text-Groesse muss neu geholt werden + //! unterscheiden, und den Text nicht neu vom Numberformatter holen? + + pLastCell = NULL; +} + +void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet ) +{ + nMaxDigitWidth = 0; + nSignWidth = 0; + nDotWidth = 0; + nExpWidth = 0; + // wird gerufen, wenn sich die Font-Variablen nicht aendern (!StringDiffer) + + pPattern = pNew; + pCondSet = pSet; //! noetig ??? + + // Zahlenformat + + ULONG nOld = nValueFormat; +// nValueFormat = pPattern->GetNumberFormat( pFormatter ); + const SfxPoolItem* pFormItem; + if ( !pCondSet || pCondSet->GetItemState(ATTR_VALUE_FORMAT,TRUE,&pFormItem) != SFX_ITEM_SET ) + pFormItem = &pPattern->GetItem(ATTR_VALUE_FORMAT); + const SfxPoolItem* pLangItem; + if ( !pCondSet || pCondSet->GetItemState(ATTR_LANGUAGE_FORMAT,TRUE,&pLangItem) != SFX_ITEM_SET ) + pLangItem = &pPattern->GetItem(ATTR_LANGUAGE_FORMAT); + nValueFormat = pOutput->pDoc->GetFormatTable()->GetFormatForLanguageIfBuiltIn( + ((SfxUInt32Item*)pFormItem)->GetValue(), + ((SvxLanguageItem*)pLangItem)->GetLanguage() ); + + if (nValueFormat != nOld) + pLastCell = NULL; // immer neu formatieren + + // Raender + + pMargin = (const SvxMarginItem*)&pPattern->GetItem( ATTR_MARGIN, pCondSet ); + + if ( eAttrHorJust == SVX_HOR_JUSTIFY_LEFT ) + nIndent = ((const SfxUInt16Item&)pPattern->GetItem( ATTR_INDENT, pCondSet )).GetValue(); + else + nIndent = 0; + + // "Shrink to fit" + + bShrink = static_cast<const SfxBoolItem&>(pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue(); +} + +inline BOOL SameValue( ScBaseCell* pCell, ScBaseCell* pOldCell ) // pCell ist != 0 +{ + return pOldCell && pOldCell->GetCellType() == CELLTYPE_VALUE && + pCell->GetCellType() == CELLTYPE_VALUE && + ((ScValueCell*)pCell)->GetValue() == ((ScValueCell*)pOldCell)->GetValue(); +} + +BOOL ScDrawStringsVars::SetText( ScBaseCell* pCell ) +{ + BOOL bChanged = FALSE; + + if (pCell) + { + if ( !SameValue( pCell, pLastCell ) ) + { + pLastCell = pCell; // Zelle merken + + Color* pColor; + ULONG nFormat = GetValueFormat(); + ScCellFormat::GetString( pCell, + nFormat, aString, &pColor, + *pOutput->pDoc->GetFormatTable(), + pOutput->bShowNullValues, + pOutput->bShowFormulas, + ftCheck ); + + if (aString.Len() > DRAWTEXT_MAX) + aString.Erase(DRAWTEXT_MAX); + + if ( pColor && !pOutput->bSyntaxMode && !( pOutput->bUseStyleColor && pOutput->bForceAutoColor ) ) + { + OutputDevice* pDev = pOutput->pDev; + aFont.SetColor(*pColor); + pDev->SetFont( aFont ); // nur fuer Ausgabe + bChanged = TRUE; + pLastCell = NULL; // naechstes Mal wieder hierherkommen + } + + TextChanged(); + } + // sonst String/Groesse behalten + } + else + { + aString.Erase(); + pLastCell = NULL; + aTextSize = Size(0,0); + nOriginalWidth = 0; + } + + return bChanged; +} + +void ScDrawStringsVars::SetHashText() +{ + SetAutoText( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("###")) ); +} + +void ScDrawStringsVars::SetTextToWidthOrHash( ScBaseCell* pCell, long nWidth ) +{ + // #i113045# do the single-character width calculations in logic units + if (bPixelToLogic) + nWidth = pOutput->pRefDevice->PixelToLogic(Size(nWidth,0)).Width(); + + if (!pCell) + return; + + CellType eType = pCell->GetCellType(); + if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA) + // must be a value or formula cell. + return; + + if (eType == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); + if (pFCell->GetErrCode() != 0) + { + SetHashText(); // If the error string doesn't fit, always use "###" + return; + } + // If it's formula, the result must be a value. + if (!pFCell->IsValue()) + return; + } + + ULONG nFormat = GetValueFormat(); + if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) + { + // Not 'General' number format. Set hash text and bail out. + SetHashText(); + return; + } + + double fVal = (eType == CELLTYPE_VALUE) ? + static_cast<ScValueCell*>(pCell)->GetValue() : static_cast<ScFormulaCell*>(pCell)->GetValue(); + + const SvNumberformat* pNumFormat = pOutput->pDoc->GetFormatTable()->GetEntry(nFormat); + if (!pNumFormat) + return; + + long nMaxDigit = GetMaxDigitWidth(); + sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit); + + if (!pNumFormat->GetOutputString(fVal, nNumDigits, aString)) + // Failed to get output string. Bail out. + return; + + sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0; + xub_StrLen nLen = aString.Len(); + sal_Unicode cDecSep = ScGlobal::GetpLocaleData()->getLocaleItem().decimalSeparator.getStr()[0]; + for (xub_StrLen i = 0; i < nLen; ++i) + { + sal_Unicode c = aString.GetChar(i); + if (c == sal_Unicode('-')) + ++nSignCount; + else if (c == cDecSep) + ++nDecimalCount; + else if (c == sal_Unicode('E')) + ++nExpCount; + } + + // #i112250# A small value might be formatted as "0" when only counting the digits, + // but fit into the column when considering the smaller width of the decimal separator. + if (aString.EqualsAscii("0") && fVal != 0.0) + nDecimalCount = 1; + + if (nDecimalCount) + nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount; + if (nSignCount) + nWidth += (nMaxDigit - GetSignWidth()) * nSignCount; + if (nExpCount) + nWidth += (nMaxDigit - GetExpWidth()) * nExpCount; + + if (nDecimalCount || nSignCount || nExpCount) + { + // Re-calculate. + nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit); + if (!pNumFormat->GetOutputString(fVal, nNumDigits, aString)) + // Failed to get output string. Bail out. + return; + } + + long nActualTextWidth = pOutput->pFmtDevice->GetTextWidth(aString); + if (nActualTextWidth > nWidth) + { + // Even after the decimal adjustment the text doesn't fit. Give up. + SetHashText(); + return; + } + + TextChanged(); + pLastCell = NULL; // #i113022# equal cell and format in another column may give different string +} + +void ScDrawStringsVars::SetAutoText( const String& rAutoText ) +{ + aString = rAutoText; + + OutputDevice* pRefDevice = pOutput->pRefDevice; + OutputDevice* pFmtDevice = pOutput->pFmtDevice; + aTextSize.Width() = pFmtDevice->GetTextWidth( aString ); + aTextSize.Height() = pFmtDevice->GetTextHeight(); + + if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER ) + { + double fMul = pOutput->GetStretch(); + aTextSize.Width() = (long)(aTextSize.Width() / fMul + 0.5); + } + + aTextSize.Height() = aMetric.GetAscent() + aMetric.GetDescent(); + if ( GetOrient() != SVX_ORIENTATION_STANDARD ) + { + long nTemp = aTextSize.Height(); + aTextSize.Height() = aTextSize.Width(); + aTextSize.Width() = nTemp; + } + + nOriginalWidth = aTextSize.Width(); + if ( bPixelToLogic ) + aTextSize = pRefDevice->LogicToPixel( aTextSize ); + + pLastCell = NULL; // derselbe Text kann in der naechsten Zelle wieder passen +} + +long ScDrawStringsVars::GetMaxDigitWidth() +{ + if (nMaxDigitWidth > 0) + return nMaxDigitWidth; + + sal_Char cZero = '0'; + for (sal_Char i = 0; i < 10; ++i) + { + sal_Char cDigit = cZero + i; + long n = pOutput->pFmtDevice->GetTextWidth(String(cDigit)); + nMaxDigitWidth = ::std::max(nMaxDigitWidth, n); + } + return nMaxDigitWidth; +} + +long ScDrawStringsVars::GetSignWidth() +{ + if (nSignWidth > 0) + return nSignWidth; + + nSignWidth = pOutput->pFmtDevice->GetTextWidth(String('-')); + return nSignWidth; +} + +long ScDrawStringsVars::GetDotWidth() +{ + if (nDotWidth > 0) + return nDotWidth; + + const ::rtl::OUString& sep = ScGlobal::GetpLocaleData()->getLocaleItem().decimalSeparator; + nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep); + return nDotWidth; +} + +long ScDrawStringsVars::GetExpWidth() +{ + if (nExpWidth > 0) + return nExpWidth; + + nExpWidth = pOutput->pFmtDevice->GetTextWidth(String('E')); + return nExpWidth; +} + +void ScDrawStringsVars::TextChanged() +{ + OutputDevice* pRefDevice = pOutput->pRefDevice; + OutputDevice* pFmtDevice = pOutput->pFmtDevice; + aTextSize.Width() = pFmtDevice->GetTextWidth( aString ); + aTextSize.Height() = pFmtDevice->GetTextHeight(); + + if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER ) + { + double fMul = pOutput->GetStretch(); + aTextSize.Width() = (long)(aTextSize.Width() / fMul + 0.5); + } + + aTextSize.Height() = aMetric.GetAscent() + aMetric.GetDescent(); + if ( GetOrient() != SVX_ORIENTATION_STANDARD ) + { + long nTemp = aTextSize.Height(); + aTextSize.Height() = aTextSize.Width(); + aTextSize.Width() = nTemp; + } + + nOriginalWidth = aTextSize.Width(); + if ( bPixelToLogic ) + aTextSize = pRefDevice->LogicToPixel( aTextSize ); +} + +BOOL ScDrawStringsVars::HasEditCharacters() const +{ + static const sal_Unicode pChars[] = + { + CHAR_NBSP, CHAR_SHY, CHAR_ZWSP, CHAR_LRM, CHAR_RLM, CHAR_NBHY, CHAR_ZWNBSP, 0 + }; + return aString.SearchChar( pChars ) != STRING_NOTFOUND; +} + +//================================================================== + +double ScOutputData::GetStretch() +{ + if ( pRefDevice->IsMapMode() ) + { + // #95920# If a non-trivial MapMode is set, its scale is now already + // taken into account in the OutputDevice's font handling + // (OutputDevice::ImplNewFont, see #95414#). + // The old handling below is only needed for pixel output. + return 1.0; + } + + // calculation in double is faster than Fraction multiplication + // and doesn't overflow + + if ( pRefDevice == pFmtDevice ) + { + MapMode aOld = pRefDevice->GetMapMode(); + return ((double)aOld.GetScaleY()) / ((double)aOld.GetScaleX()) * ((double)aZoomY) / ((double)aZoomX); + } + else + { + // when formatting for printer, device map mode has already been taken care of + return ((double)aZoomY) / ((double)aZoomX); + } +} + +//================================================================== + +// +// output strings +// + +void lcl_DoHyperlinkResult( OutputDevice* pDev, const Rectangle& rRect, ScBaseCell* pCell ) +{ + vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() ); + + String aCellText; + String aURL; + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) + { + ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); + if ( pFCell->IsHyperLinkCell() ) + pFCell->GetURLResult( aURL, aCellText ); + } + + if ( aURL.Len() && pPDFData ) + { + vcl::PDFExtOutDevBookmarkEntry aBookmark; + aBookmark.nLinkId = pPDFData->CreateLink( rRect ); + aBookmark.aBookmark = aURL; + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks(); + rBookmarks.push_back( aBookmark ); + } +} + +void ScOutputData::SetSyntaxColor( Font* pFont, ScBaseCell* pCell ) +{ + if (pCell) + { + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + pFont->SetColor( *pValueColor ); + break; + case CELLTYPE_STRING: + pFont->SetColor( *pTextColor ); + break; + case CELLTYPE_FORMULA: + pFont->SetColor( *pFormulaColor ); + break; + default: + { + // added to avoid warnings + } + } + } +} + +void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor ) +{ + ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 ); + SfxItemSet aSet( rEngine.GetEmptyItemSet() ); + aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) ); + rEngine.QuickSetAttribs( aSet, aSel ); + // function is called with update mode set to FALSE +} + +void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, ScBaseCell* pCell ) +{ + if (pCell) + { + Color aColor; + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + aColor = *pValueColor; + break; + case CELLTYPE_STRING: + aColor = *pTextColor; + break; + case CELLTYPE_FORMULA: + aColor = *pFormulaColor; + break; + default: + { + // added to avoid warnings + } + } + lcl_SetEditColor( rEngine, aColor ); + } +} + +BOOL ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY, + SCCOL& rOverX, SCROW& rOverY, + BOOL bVisRowChanged ) +{ + BOOL bDoMerge = FALSE; + BOOL bIsLeft = ( nX == nVisX1 ); + BOOL bIsTop = ( nY == nVisY1 ) || bVisRowChanged; + + CellInfo* pInfo = &pRowInfo[nArrY].pCellInfo[nX+1]; + if ( pInfo->bHOverlapped && pInfo->bVOverlapped ) + bDoMerge = bIsLeft && bIsTop; + else if ( pInfo->bHOverlapped ) + bDoMerge = bIsLeft; + else if ( pInfo->bVOverlapped ) + bDoMerge = bIsTop; + + // weiter solange versteckt +/* if (!bDoMerge) + return FALSE; +*/ + + rOverX = nX; + rOverY = nY; + BOOL bHOver = pInfo->bHOverlapped; + BOOL bVOver = pInfo->bVOverlapped; + BOOL bHidden; + + while (bHOver) // nY konstant + { + --rOverX; + bHidden = pDoc->ColHidden(rOverX, nTab); + if ( !bDoMerge && !bHidden ) + return FALSE; + + if (rOverX >= nX1 && !bHidden) + { +// rVirtPosX -= pRowInfo[0].pCellInfo[rOverX+1].nWidth; + bHOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bHOverlapped; + bVOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bVOverlapped; + } + else + { +// if (!bClipVirt) +// rVirtPosX -= (long) (pDoc->GetColWidth( rOverX, nTab ) * nPPTX); + USHORT nOverlap = ((ScMergeFlagAttr*)pDoc->GetAttr( + rOverX, rOverY, nTab, ATTR_MERGE_FLAG ))->GetValue(); + bHOver = ((nOverlap & SC_MF_HOR) != 0); + bVOver = ((nOverlap & SC_MF_VER) != 0); + } + } + + while (bVOver) + { + --rOverY; + bHidden = pDoc->RowHidden(rOverY, nTab); + if ( !bDoMerge && !bHidden ) + return FALSE; + + if (nArrY>0) + --nArrY; // lokale Kopie ! + + if (rOverX >= nX1 && rOverY >= nY1 && + !pDoc->ColHidden(rOverX, nTab) && + !pDoc->RowHidden(rOverY, nTab) && + pRowInfo[nArrY].nRowNo == rOverY) + { +// rVirtPosY -= pRowInfo[nArrY].nHeight; + bHOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bHOverlapped; + bVOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bVOverlapped; + } + else + { +// if (!bClipVirt) +// rVirtPosY -= (long) (pDoc->GetRowHeight( rOverY, nTab ) * nPPTY); + USHORT nOverlap = ((ScMergeFlagAttr*)pDoc->GetAttr( + rOverX, rOverY, nTab, ATTR_MERGE_FLAG ))->GetValue(); + bHOver = ((nOverlap & SC_MF_HOR) != 0); + bVOver = ((nOverlap & SC_MF_VER) != 0); + } + } + + return TRUE; +} + +inline BOOL StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr*& rpNewPattern ) +{ + DBG_ASSERT( rpNewPattern, "pNewPattern" ); + + if ( rpNewPattern == rpOldPattern ) + return FALSE; + else if ( !rpOldPattern ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT ) != &rpOldPattern->GetItem( ATTR_FONT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_FONT_POSTURE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_UNDERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_OVERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_WORDLINE ) != &rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ) != &rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_CONTOUR ) != &rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_SHADOWED ) != &rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_COLOR ) != &rpOldPattern->GetItem( ATTR_FONT_COLOR ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_HOR_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_VER_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_STACKED ) != &rpOldPattern->GetItem( ATTR_STACKED ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_LINEBREAK ) != &rpOldPattern->GetItem( ATTR_LINEBREAK ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_MARGIN ) != &rpOldPattern->GetItem( ATTR_MARGIN ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_ROTATE_VALUE ) != &rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FORBIDDEN_RULES ) != &rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ) != &rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_FONT_RELIEF ) != &rpOldPattern->GetItem( ATTR_FONT_RELIEF ) ) + return TRUE; + else if ( &rpNewPattern->GetItem( ATTR_BACKGROUND ) != &rpOldPattern->GetItem( ATTR_BACKGROUND ) ) + return TRUE; // needed with automatic text color + else + { + rpOldPattern = rpNewPattern; + return FALSE; + } +} + +inline void lcl_CreateInterpretProgress( BOOL& bProgress, ScDocument* pDoc, + ScFormulaCell* pFCell ) +{ + if ( !bProgress && pFCell->GetDirty() ) + { + ScProgress::CreateInterpretProgress( pDoc, TRUE ); + bProgress = TRUE; + } +} + +inline BYTE GetScriptType( ScDocument* pDoc, ScBaseCell* pCell, + const ScPatternAttr* pPattern, + const SfxItemSet* pCondSet ) +{ + return pDoc->GetCellScriptType( pCell, pPattern->GetNumberFormat( pDoc->GetFormatTable(), pCondSet ) ); +} + +inline BOOL IsAmbiguousScript( BYTE nScript ) +{ + return ( nScript != SCRIPTTYPE_LATIN && + nScript != SCRIPTTYPE_ASIAN && + nScript != SCRIPTTYPE_COMPLEX ); +} + +BOOL ScOutputData::IsEmptyCellText( RowInfo* pThisRowInfo, SCCOL nX, SCROW nY ) +{ + // pThisRowInfo may be NULL + + BOOL bEmpty; + if ( pThisRowInfo && nX <= nX2 ) + bEmpty = pThisRowInfo->pCellInfo[nX+1].bEmptyCellText; + else + bEmpty = ( pDoc->GetCell( ScAddress( nX, nY, nTab ) ) == NULL ); + + if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) ) + { + // for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated + // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun) + + BOOL bIsPrint = ( eType == OUTTYPE_PRINTER ); + + if ( bIsPrint || bTabProtected ) + { + const ScProtectionAttr* pAttr = (const ScProtectionAttr*) + pDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION ); + if ( bIsPrint && pAttr->GetHidePrint() ) + bEmpty = TRUE; + else if ( bTabProtected ) + { + if ( pAttr->GetHideCell() ) + bEmpty = TRUE; + else if ( bShowFormulas && pAttr->GetHideFormula() ) + { + ScBaseCell* pCell = pDoc->GetCell( ScAddress( nX, nY, nTab ) ); + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) + bEmpty = TRUE; + } + } + } + } + return bEmpty; +} + +void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScBaseCell*& rpCell ) +{ + pDoc->GetCell( nCol, nRow, nTabP, rpCell ); + if ( rpCell && IsEmptyCellText( NULL, nCol, nRow ) ) + rpCell = NULL; +} + +BOOL ScOutputData::IsAvailable( SCCOL nX, SCROW nY ) +{ + // apply the same logic here as in DrawStrings/DrawEdit: + // Stop at non-empty or merged or overlapped cell, + // where a note is empty as well as a cell that's hidden by protection settings + + const ScBaseCell* pCell = pDoc->GetCell( ScAddress( nX, nY, nTab ) ); + if ( pCell && pCell->GetCellType() != CELLTYPE_NOTE && !IsEmptyCellText( NULL, nX, nY ) ) + { + return FALSE; + } + + const ScPatternAttr* pPattern = pDoc->GetPattern( nX, nY, nTab ); + if ( ((const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE)).IsMerged() || + ((const ScMergeFlagAttr&)pPattern->GetItem(ATTR_MERGE_FLAG)).IsOverlapped() ) + { + return FALSE; + } + + return TRUE; +} + +// nX, nArrY: loop variables from DrawStrings / DrawEdit +// nPosX, nPosY: corresponding positions for nX, nArrY +// nCellX, nCellY: position of the cell that contains the text +// nNeeded: Text width, including margin +// rPattern: cell format at nCellX, nCellY +// nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings +// bCellIsValue: if set, don't extend into empty cells +// bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set) +// bOverwrite: if set, also extend into non-empty cells (for rotated text) +// rParam output: various area parameters. + +void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, long nPosX, long nPosY, + SCCOL nCellX, SCROW nCellY, long nNeeded, + const ScPatternAttr& rPattern, + USHORT nHorJustify, bool bCellIsValue, + bool bBreak, bool bOverwrite, + OutputAreaParam& rParam ) +{ + // rThisRowInfo may be for a different row than nCellY, is still used for clip marks + RowInfo& rThisRowInfo = pRowInfo[nArrY]; + + long nLayoutSign = bLayoutRTL ? -1 : 1; + + long nCellPosX = nPosX; // find nCellX position, starting at nX/nPosX + SCCOL nCompCol = nX; + while ( nCellX > nCompCol ) + { + //! extra member function for width? + long nColWidth = ( nCompCol <= nX2 ) ? + pRowInfo[0].pCellInfo[nCompCol+1].nWidth : + (long) ( pDoc->GetColWidth( nCompCol, nTab ) * nPPTX ); + nCellPosX += nColWidth * nLayoutSign; + ++nCompCol; + } + while ( nCellX < nCompCol ) + { + --nCompCol; + long nColWidth = ( nCompCol <= nX2 ) ? + pRowInfo[0].pCellInfo[nCompCol+1].nWidth : + (long) ( pDoc->GetColWidth( nCompCol, nTab ) * nPPTX ); + nCellPosX -= nColWidth * nLayoutSign; + } + + long nCellPosY = nPosY; // find nCellY position, starting at nArrY/nPosY + SCSIZE nCompArr = nArrY; + SCROW nCompRow = pRowInfo[nCompArr].nRowNo; + while ( nCellY > nCompRow ) + { + if ( nCompArr + 1 < nArrCount ) + { + nCellPosY += pRowInfo[nCompArr].nHeight; + ++nCompArr; + nCompRow = pRowInfo[nCompArr].nRowNo; + } + else + { + USHORT nDocHeight = pDoc->GetRowHeight( nCompRow, nTab ); + if ( nDocHeight ) + nCellPosY += (long) ( nDocHeight * nPPTY ); + ++nCompRow; + } + } + nCellPosY -= (long) pDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, nPPTY ); + + const ScMergeAttr* pMerge = (const ScMergeAttr*)&rPattern.GetItem( ATTR_MERGE ); + BOOL bMerged = pMerge->IsMerged(); + long nMergeCols = pMerge->GetColMerge(); + if ( nMergeCols == 0 ) + nMergeCols = 1; + long nMergeRows = pMerge->GetRowMerge(); + if ( nMergeRows == 0 ) + nMergeRows = 1; + + long i; + long nMergeSizeX = 0; + for ( i=0; i<nMergeCols; i++ ) + { + long nColWidth = ( nCellX+i <= nX2 ) ? + pRowInfo[0].pCellInfo[nCellX+i+1].nWidth : + (long) ( pDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * nPPTX ); + nMergeSizeX += nColWidth; + } + long nMergeSizeY = 0; + short nDirect = 0; + if ( rThisRowInfo.nRowNo == nCellY ) + { + // take first row's height from row info + nMergeSizeY += rThisRowInfo.nHeight; + nDirect = 1; // skip in loop + } + // following rows always from document + nMergeSizeY += (long) pDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, nPPTY); + + --nMergeSizeX; // leave out the grid horizontally, also for alignment (align between grid lines) + + rParam.mnColWidth = nMergeSizeX; // store the actual column width. + + // + // construct the rectangles using logical left/right values (justify is called at the end) + // + + // rAlignRect is the single cell or merged area, used for alignment. + + rParam.maAlignRect.Left() = nCellPosX; + rParam.maAlignRect.Right() = nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign; + rParam.maAlignRect.Top() = nCellPosY; + rParam.maAlignRect.Bottom() = nCellPosY + nMergeSizeY - 1; + + // rClipRect is all cells that are used for output. + // For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used. + + rParam.maClipRect = rParam.maAlignRect; + if ( nNeeded > nMergeSizeX ) + { + SvxCellHorJustify eHorJust = (SvxCellHorJustify)nHorJustify; + + long nMissing = nNeeded - nMergeSizeX; + long nLeftMissing = 0; + long nRightMissing = 0; + switch ( eHorJust ) + { + case SVX_HOR_JUSTIFY_LEFT: + nRightMissing = nMissing; + break; + case SVX_HOR_JUSTIFY_RIGHT: + nLeftMissing = nMissing; + break; + case SVX_HOR_JUSTIFY_CENTER: + nLeftMissing = nMissing / 2; + nRightMissing = nMissing - nLeftMissing; + break; + default: + { + // added to avoid warnings + } + } + + // nLeftMissing, nRightMissing are logical, eHorJust values are visual + if ( bLayoutRTL ) + ::std::swap( nLeftMissing, nRightMissing ); + + SCCOL nRightX = nCellX; + SCCOL nLeftX = nCellX; + if ( !bMerged && !bCellIsValue && !bBreak ) + { + // look for empty cells into which the text can be extended + + while ( nRightMissing > 0 && nRightX < MAXCOL && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) ) + { + ++nRightX; + long nAdd = (long) ( pDoc->GetColWidth( nRightX, nTab ) * nPPTX ); + nRightMissing -= nAdd; + rParam.maClipRect.Right() += nAdd * nLayoutSign; + + if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 ) + rThisRowInfo.pCellInfo[nRightX].bHideGrid = TRUE; + } + + while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) ) + { + if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 ) + rThisRowInfo.pCellInfo[nLeftX].bHideGrid = TRUE; + + --nLeftX; + long nAdd = (long) ( pDoc->GetColWidth( nLeftX, nTab ) * nPPTX ); + nLeftMissing -= nAdd; + rParam.maClipRect.Left() -= nAdd * nLayoutSign; + } + } + + // Set flag and reserve space for clipping mark triangle, + // even if rThisRowInfo isn't for nCellY (merged cells). + if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue ) + { + rThisRowInfo.pCellInfo[nRightX+1].nClipMark |= SC_CLIPMARK_RIGHT; + bAnyClipped = TRUE; + long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX ); + rParam.maClipRect.Right() -= nMarkPixel * nLayoutSign; + } + if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue ) + { + rThisRowInfo.pCellInfo[nLeftX+1].nClipMark |= SC_CLIPMARK_LEFT; + bAnyClipped = TRUE; + long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX ); + rParam.maClipRect.Left() += nMarkPixel * nLayoutSign; + } + + rParam.mbLeftClip = ( nLeftMissing > 0 ); + rParam.mbRightClip = ( nRightMissing > 0 ); + } + else + { + rParam.mbLeftClip = rParam.mbRightClip = FALSE; + + // leave space for AutoFilter on screen + // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize) + + if ( eType==OUTTYPE_WINDOW && + ( static_cast<const ScMergeFlagAttr&>(rPattern.GetItem(ATTR_MERGE_FLAG)).GetValue() & SC_MF_AUTO ) && + ( !bBreak || pRefDevice == pFmtDevice ) ) + { + // filter drop-down width is now independent from row height + const long nFilter = DROPDOWN_BITMAP_SIZE; + BOOL bFit = ( nNeeded + nFilter <= nMergeSizeX ); + if ( bFit || bCellIsValue ) + { + // content fits even in the remaining area without the filter button + // -> align within that remaining area + + rParam.maAlignRect.Right() -= nFilter * nLayoutSign; + rParam.maClipRect.Right() -= nFilter * nLayoutSign; + + // if a number doesn't fit, don't hide part of the number behind the button + // -> set clip flags, so "###" replacement is used (but also within the smaller area) + + if ( !bFit ) + rParam.mbLeftClip = rParam.mbRightClip = TRUE; + } + } + } + + // justify both rectangles for alignment calculation, use with DrawText etc. + + rParam.maAlignRect.Justify(); + rParam.maClipRect.Justify(); + +#if 0 + //! Test !!! + pDev->Push(); + pDev->SetLineColor(); + pDev->SetFillColor( COL_LIGHTGREEN ); + pDev->DrawRect( pDev->PixelToLogic(rParam.maClipRect) ); + pDev->DrawRect( rParam.maClipRect ); // print preview + pDev->Pop(); + //! Test !!! +#endif +} + +void ScOutputData::DrawStrings( BOOL bPixelToLogic ) +{ + DBG_ASSERT( pDev == pRefDevice || + pDev->GetMapMode().GetMapUnit() == pRefDevice->GetMapMode().GetMapUnit(), + "DrawStrings: unterschiedliche MapUnits ?!?!" ); + + vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() ); + + BOOL bWasIdleDisabled = pDoc->IsIdleDisabled(); + pDoc->DisableIdle( TRUE ); + Size aMinSize = pRefDevice->PixelToLogic(Size(0,100)); // erst darueber wird ausgegeben +// UINT32 nMinHeight = aMinSize.Height() / 200; // 1/2 Pixel + + ScDrawStringsVars aVars( this, bPixelToLogic ); + + BOOL bProgress = FALSE; + + long nInitPosX = nScrX; + if ( bLayoutRTL ) + nInitPosX += nMirrorW - 1; // pixels + long nLayoutSign = bLayoutRTL ? -1 : 1; + + SCCOL nLastContentCol = MAXCOL; + if ( nX2 < MAXCOL ) + nLastContentCol = sal::static_int_cast<SCCOL>( + nLastContentCol - pDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, MAXCOL, nY2, nTab, DIR_RIGHT ) ); + SCCOL nLoopStartX = nX1; + if ( nX1 > 0 ) + --nLoopStartX; // start before nX1 for rest of long text to the left + + // variables for GetOutputArea + OutputAreaParam aAreaParam; + BOOL bCellIsValue = FALSE; + long nNeededWidth = 0; + SvxCellHorJustify eOutHorJust = SVX_HOR_JUSTIFY_STANDARD; + const ScPatternAttr* pPattern = NULL; + const SfxItemSet* pCondSet = NULL; + const ScPatternAttr* pOldPattern = NULL; + const SfxItemSet* pOldCondSet = NULL; + BYTE nOldScript = 0; + + // alternative pattern instances in case we need to modify the pattern + // before processing the cell value. + ::boost::ptr_vector<ScPatternAttr> aAltPatterns; + + long nPosY = nScrY; + for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + if ( pThisRowInfo->bChanged ) + { + SCROW nY = pThisRowInfo->nRowNo; +// long nCellHeight = (long) pThisRowInfo->nHeight; + long nPosX = nInitPosX; + if ( nLoopStartX < nX1 ) + nPosX -= pRowInfo[0].pCellInfo[nLoopStartX+1].nWidth * nLayoutSign; + for (SCCOL nX=nLoopStartX; nX<=nX2; nX++) + { + BOOL bMergeEmpty = FALSE; + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1]; + BOOL bEmpty = nX < nX1 || pInfo->bEmptyCellText; + + SCCOL nCellX = nX; // position where the cell really starts + SCROW nCellY = nY; + BOOL bDoCell = FALSE; + BOOL bNeedEdit = FALSE; + + // + // Part of a merged cell? + // + + BOOL bOverlapped = ( pInfo->bHOverlapped || pInfo->bVOverlapped ); + if ( bOverlapped ) + { + bEmpty = TRUE; + + SCCOL nOverX; // start of the merged cells + SCROW nOverY; + BOOL bVisChanged = !pRowInfo[nArrY-1].bChanged; + if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged )) + { + nCellX = nOverX; + nCellY = nOverY; + bDoCell = TRUE; + } + else + bMergeEmpty = TRUE; + } + + // + // Rest of a long text further to the left? + // + + if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped ) + { + SCCOL nTempX=nX1; + while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY )) + --nTempX; + + if ( nTempX < nX1 && + !IsEmptyCellText( pThisRowInfo, nTempX, nY ) && + !pDoc->HasAttrib( nTempX,nY,nTab, nX1,nY,nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ) ) + { + nCellX = nTempX; + bDoCell = TRUE; + } + } + + // + // Rest of a long text further to the right? + // + + if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped ) + { + // don't have to look further than nLastContentCol + + SCCOL nTempX=nX; + while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY )) + ++nTempX; + + if ( nTempX > nX && + !IsEmptyCellText( pThisRowInfo, nTempX, nY ) && + !pDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ) ) + { + nCellX = nTempX; + bDoCell = TRUE; + } + } + + // + // normal visible cell + // + + if (!bEmpty) + bDoCell = TRUE; + + // + // don't output the cell that's being edited + // + + if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow ) + bDoCell = FALSE; + + // + // output the cell text + // + + ScBaseCell* pCell = NULL; + if (bDoCell) + { + if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 ) + pCell = pThisRowInfo->pCellInfo[nCellX+1].pCell; + else + GetVisibleCell( nCellX, nCellY, nTab, pCell ); // get from document + if ( !pCell ) + bDoCell = FALSE; + else if ( pCell->GetCellType() == CELLTYPE_EDIT ) + bNeedEdit = TRUE; + } + if (bDoCell && !bNeedEdit) + { + if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) + { + CellInfo& rCellInfo = pThisRowInfo->pCellInfo[nCellX+1]; + pPattern = rCellInfo.pPatternAttr; + pCondSet = rCellInfo.pConditionSet; + + if ( !pPattern ) + { + // #i68085# pattern from cell info for hidden columns is null, + // test for null is quicker than using column flags + pPattern = pDoc->GetPattern( nCellX, nCellY, nTab ); + pCondSet = pDoc->GetCondResult( nCellX, nCellY, nTab ); + } + } + else // get from document + { + pPattern = pDoc->GetPattern( nCellX, nCellY, nTab ); + pCondSet = pDoc->GetCondResult( nCellX, nCellY, nTab ); + } + + if (pCell->HasValueData() && + static_cast<const SfxBoolItem&>( + pPattern->GetItem(ATTR_LINEBREAK, pCondSet)).GetValue()) + { + // Disable line break when the cell content is numeric. + aAltPatterns.push_back(new ScPatternAttr(*pPattern)); + ScPatternAttr* pAltPattern = &aAltPatterns.back(); + SfxBoolItem aLineBreak(ATTR_LINEBREAK, false); + pAltPattern->GetItemSet().Put(aLineBreak); + pPattern = pAltPattern; + } + + BYTE nScript = GetScriptType( pDoc, pCell, pPattern, pCondSet ); + if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType(); + if ( pPattern != pOldPattern || pCondSet != pOldCondSet || + nScript != nOldScript || bSyntaxMode ) + { + if ( StringDiffer(pOldPattern,pPattern) || + pCondSet != pOldCondSet || nScript != nOldScript || bSyntaxMode ) + aVars.SetPattern( pPattern, pCondSet, pCell, nScript ); + else + aVars.SetPatternSimple( pPattern, pCondSet ); + pOldPattern = pPattern; + pOldCondSet = pCondSet; + nOldScript = nScript; + } + + // use edit engine for rotated, stacked or mixed-script text + if ( aVars.GetOrient() == SVX_ORIENTATION_STACKED || + aVars.IsRotated() || IsAmbiguousScript(nScript) ) + bNeedEdit = TRUE; + } + if (bDoCell && !bNeedEdit) + { + BOOL bFormulaCell = (pCell->GetCellType() == CELLTYPE_FORMULA ); + if ( bFormulaCell ) + lcl_CreateInterpretProgress( bProgress, pDoc, (ScFormulaCell*)pCell ); + if ( aVars.SetText(pCell) ) + pOldPattern = NULL; + bNeedEdit = aVars.HasEditCharacters() || + (bFormulaCell && ((ScFormulaCell*)pCell)->IsMultilineResult()); + } + long nTotalMargin = 0; + if (bDoCell && !bNeedEdit) + { + CellType eCellType = pCell->GetCellType(); + bCellIsValue = ( eCellType == CELLTYPE_VALUE ); + if ( eCellType == CELLTYPE_FORMULA ) + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + bCellIsValue = pFCell->IsRunning() || pFCell->IsValue(); + } + + eOutHorJust = ( aVars.GetHorJust() != SVX_HOR_JUSTIFY_STANDARD ) ? + aVars.GetHorJust() : + ( bCellIsValue ? SVX_HOR_JUSTIFY_RIGHT : SVX_HOR_JUSTIFY_LEFT ); + + if ( eOutHorJust == SVX_HOR_JUSTIFY_BLOCK || eOutHorJust == SVX_HOR_JUSTIFY_REPEAT ) + eOutHorJust = SVX_HOR_JUSTIFY_LEFT; // repeat is not yet implemented + + BOOL bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SVX_HOR_JUSTIFY_BLOCK ); + BOOL bRepeat = aVars.IsRepeat() && !bBreak; + BOOL bShrink = aVars.IsShrink() && !bBreak && !bRepeat; + + nTotalMargin = + static_cast<long>(aVars.GetLeftTotal() * nPPTX) + + static_cast<long>(aVars.GetMargin()->GetRightMargin() * nPPTX); + + nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin; + + // GetOutputArea gives justfied rectangles + GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth, + *pPattern, sal::static_int_cast<USHORT>(eOutHorJust), + bCellIsValue || bRepeat || bShrink, bBreak, FALSE, + aAreaParam ); + + if ( bShrink ) + { + if ( aVars.GetOrient() != SVX_ORIENTATION_STANDARD ) + { + // Only horizontal scaling is handled here. + // DrawEdit is used to vertically scale 90 deg rotated text. + bNeedEdit = TRUE; + } + else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal + { + long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; + long nScaleSize = aVars.GetTextSize().Width(); // without margin + + if ( nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats) + { + long nScale = ( nAvailable * 100 ) / nScaleSize; + + aVars.SetShrinkScale( nScale, nOldScript ); + long nNewSize = aVars.GetTextSize().Width(); + + USHORT nShrinkAgain = 0; + while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) + { + // If the text is still too large, reduce the scale again by 10%, until it fits, + // at most 7 times (it's less than 50% of the calculated scale then). + + nScale = ( nScale * 9 ) / 10; + aVars.SetShrinkScale( nScale, nOldScript ); + nNewSize = aVars.GetTextSize().Width(); + ++nShrinkAgain; + } + // If even at half the size the font still isn't rendered smaller, + // fall back to normal clipping (showing ### for numbers). + if ( nNewSize <= nAvailable ) + aAreaParam.mbLeftClip = aAreaParam.mbRightClip = FALSE; + + pOldPattern = NULL; + } + } + } + + if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip ) + { + long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; + long nRepeatSize = aVars.GetTextSize().Width(); // without margin + // When formatting for the printer, the text sizes don't always add up. + // Round down (too few repetitions) rather than exceeding the cell size then: + if ( pFmtDevice != pRefDevice ) + ++nRepeatSize; + if ( nRepeatSize > 0 ) + { + long nRepeatCount = nAvailable / nRepeatSize; + if ( nRepeatCount > 1 ) + { + String aCellStr = aVars.GetString(); + String aRepeated = aCellStr; + for ( long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) + aRepeated.Append( aCellStr ); + aVars.SetAutoText( aRepeated ); + } + } + } + + // use edit engine if automatic line breaks are needed + if ( bBreak ) + { + if ( aVars.GetOrient() == SVX_ORIENTATION_STANDARD ) + bNeedEdit = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ); + else + { + long nHeight = aVars.GetTextSize().Height() + + (long)(aVars.GetMargin()->GetTopMargin()*nPPTY) + + (long)(aVars.GetMargin()->GetBottomMargin()*nPPTY); + bNeedEdit = ( nHeight > aAreaParam.maClipRect.GetHeight() ); + } + } + } + if (bNeedEdit) + { + // mark the cell in CellInfo to be drawn in DrawEdit: + // Cells to the left are marked directly, cells to the + // right are handled by the flag for nX2 + SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2; + RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0]; + pMarkRowInfo->pCellInfo[nMarkX+1].bEditEngine = TRUE; + bDoCell = FALSE; // don't draw here + } + if ( bDoCell ) + { + if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) + { + // Adjust the decimals to fit the available column width. + aVars.SetTextToWidthOrHash(pCell, aAreaParam.mnColWidth - nTotalMargin); + nNeededWidth = aVars.GetTextSize().Width() + + (long) ( aVars.GetLeftTotal() * nPPTX ) + + (long) ( aVars.GetMargin()->GetRightMargin() * nPPTX ); + if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() ) + aAreaParam.mbLeftClip = aAreaParam.mbRightClip = FALSE; + + // If the "###" replacement doesn't fit into the cells, no clip marks + // are shown, as the "###" already denotes too little space. + // The rectangles from the first GetOutputArea call remain valid. + } + + long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added + long nJustPosY = aAreaParam.maAlignRect.Top(); + long nAvailWidth = aAreaParam.maAlignRect.GetWidth(); + long nOutHeight = aAreaParam.maAlignRect.GetHeight(); + + BOOL bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW ); + if ( aAreaParam.maClipRect.Left() < nScrX ) + { + aAreaParam.maClipRect.Left() = nScrX; + aAreaParam.mbLeftClip = TRUE; + } + if ( aAreaParam.maClipRect.Right() > nScrX + nScrW ) + { + aAreaParam.maClipRect.Right() = nScrX + nScrW; //! minus one? + aAreaParam.mbRightClip = TRUE; + } + + BOOL bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip; + BOOL bVClip = FALSE; + + if ( aAreaParam.maClipRect.Top() < nScrY ) + { + aAreaParam.maClipRect.Top() = nScrY; + bVClip = TRUE; + } + if ( aAreaParam.maClipRect.Bottom() > nScrY + nScrH ) + { + aAreaParam.maClipRect.Bottom() = nScrY + nScrH; //! minus one? + bVClip = TRUE; + } + + // + // horizontalen Platz testen + // + + BOOL bRightAdjusted = FALSE; // to correct text width calculation later + BOOL bNeedEditEngine = FALSE; + if ( !bNeedEditEngine && !bOutside ) + { + switch (eOutHorJust) + { + case SVX_HOR_JUSTIFY_LEFT: + nJustPosX += (long) ( aVars.GetLeftTotal() * nPPTX ); + break; + case SVX_HOR_JUSTIFY_RIGHT: + nJustPosX += nAvailWidth - aVars.GetTextSize().Width() - + (long) ( aVars.GetMargin()->GetRightMargin() * nPPTX ); + bRightAdjusted = TRUE; + break; + case SVX_HOR_JUSTIFY_CENTER: + nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() + + (long) ( aVars.GetLeftTotal() * nPPTX ) - + (long) ( aVars.GetMargin()->GetRightMargin() * nPPTX ) ) / 2; + break; + default: + { + // added to avoid warnings + } + } + + long nTestClipHeight = aVars.GetTextSize().Height(); + switch (aVars.GetVerJust()) + { + case SVX_VER_JUSTIFY_TOP: + { + long nTop = (long)( aVars.GetMargin()->GetTopMargin() * nPPTY ); + nJustPosY += nTop; + nTestClipHeight += nTop; + } + break; + case SVX_VER_JUSTIFY_BOTTOM: + { + long nBot = (long)( aVars.GetMargin()->GetBottomMargin() * nPPTY ); + nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot; + nTestClipHeight += nBot; + } + break; + case SVX_VER_JUSTIFY_CENTER: + { + long nTop = (long)( aVars.GetMargin()->GetTopMargin() * nPPTY ); + long nBot = (long)( aVars.GetMargin()->GetBottomMargin() * nPPTY ); + nJustPosY += ( nOutHeight + nTop - + aVars.GetTextSize().Height() - nBot ) / 2; + nTestClipHeight += Abs( nTop - nBot ); + } + break; + default: + { + // added to avoid warnings + } + } + + if ( nTestClipHeight > nOutHeight ) + { + // kein vertikales Clipping beim Drucken von Zellen mit + // optimaler Hoehe, ausser bei Groesse in bedingter Formatierung + if ( eType != OUTTYPE_PRINTER || + ( pDoc->GetRowFlags( nCellY, nTab ) & CR_MANUALSIZE ) || + ( aVars.HasCondHeight() ) ) + bVClip = TRUE; + } + + if ( bHClip || bVClip ) + { + // nur die betroffene Dimension clippen, + // damit bei nicht-proportionalem Resize nicht alle + // rechtsbuendigen Zahlen abgeschnitten werden: + + if (!bHClip) + { + aAreaParam.maClipRect.Left() = nScrX; + aAreaParam.maClipRect.Right() = nScrX+nScrW; + } + if (!bVClip) + { + aAreaParam.maClipRect.Top() = nScrY; + aAreaParam.maClipRect.Bottom() = nScrY+nScrH; + } + + // aClipRect is not used after SetClipRegion/IntersectClipRegion, + // so it can be modified here + if (bPixelToLogic) + aAreaParam.maClipRect = pRefDevice->PixelToLogic( aAreaParam.maClipRect ); + + if (bMetaFile) + { + pDev->Push(); + pDev->IntersectClipRegion( aAreaParam.maClipRect ); + } + else + pDev->SetClipRegion( Region( aAreaParam.maClipRect ) ); + } + + Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation + + switch (aVars.GetOrient()) + { + case SVX_ORIENTATION_STANDARD: + nJustPosY += aVars.GetAscent(); + break; + case SVX_ORIENTATION_TOPBOTTOM: + nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent(); + break; + case SVX_ORIENTATION_BOTTOMTOP: + nJustPosY += aVars.GetTextSize().Height(); + nJustPosX += aVars.GetAscent(); + break; + default: + { + // added to avoid warnings + } + } + + // When clipping, the visible part is now completely defined by the alignment, + // there's no more special handling to show the right part of RTL text. + + Point aDrawTextPos( nJustPosX, nJustPosY ); + if ( bPixelToLogic ) + { + // undo text width adjustment in pixels + if (bRightAdjusted) + aDrawTextPos.X() += aVars.GetTextSize().Width(); + + aDrawTextPos = pRefDevice->PixelToLogic( aDrawTextPos ); + + // redo text width adjustment in logic units + if (bRightAdjusted) + aDrawTextPos.X() -= aVars.GetOriginalWidth(); + } + + // in Metafiles immer DrawTextArray, damit die Positionen mit + // aufgezeichnet werden (fuer nicht-proportionales Resize): + + String aString = aVars.GetString(); + if (bMetaFile || pFmtDevice != pDev || aZoomX != aZoomY) + { + sal_Int32* pDX = new sal_Int32[aString.Len()]; + pFmtDevice->GetTextArray( aString, pDX ); + + if ( !pRefDevice->GetConnectMetaFile() || + pRefDevice->GetOutDevType() == OUTDEV_PRINTER ) + { + double fMul = GetStretch(); + xub_StrLen nLen = aString.Len(); + for (xub_StrLen i=0; i<nLen; i++) + pDX[i] = (long)(pDX[i] / fMul + 0.5); + } + + pDev->DrawTextArray( aDrawTextPos, aString, pDX ); + delete[] pDX; + } + else + pDev->DrawText( aDrawTextPos, aString ); + + if ( bHClip || bVClip ) + { + if (bMetaFile) + pDev->Pop(); + else + pDev->SetClipRegion(); + } + + // PDF: whole-cell hyperlink from formula? + BOOL bHasURL = pPDFData && pCell && pCell->GetCellType() == CELLTYPE_FORMULA && + static_cast<ScFormulaCell*>(pCell)->IsHyperLinkCell(); + if ( bHasURL ) + { + Rectangle aURLRect( aURLStart, aVars.GetTextSize() ); + lcl_DoHyperlinkResult( pDev, aURLRect, pCell ); + } + } + } + nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign; + } + } + nPosY += pRowInfo[nArrY].nHeight; + } + if ( bProgress ) + ScProgress::DeleteInterpretProgress(); + pDoc->DisableIdle( bWasIdleDisabled ); +} + +// ------------------------------------------------------------------------------- + +void lcl_ClearEdit( EditEngine& rEngine ) // Text und Attribute +{ + rEngine.SetUpdateMode( FALSE ); + + rEngine.SetText(EMPTY_STRING); + // keine Para-Attribute uebrigbehalten... + const SfxItemSet& rPara = rEngine.GetParaAttribs(0); + if (rPara.Count()) + rEngine.SetParaAttribs( 0, + SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) ); +} + +BOOL lcl_SafeIsValue( ScBaseCell* pCell ) +{ + if (!pCell) + return FALSE; + + BOOL bRet = FALSE; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE: + bRet = TRUE; + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if ( pFCell->IsRunning() || pFCell->IsValue() ) + bRet = TRUE; + } + break; + default: + { + // added to avoid warnings + } + } + return bRet; +} + +void lcl_ScaleFonts( EditEngine& rEngine, long nPercent ) +{ + BOOL bUpdateMode = rEngine.GetUpdateMode(); + if ( bUpdateMode ) + rEngine.SetUpdateMode( FALSE ); + + USHORT nParCount = rEngine.GetParagraphCount(); + for (USHORT nPar=0; nPar<nParCount; nPar++) + { + SvUShorts aPortions; + rEngine.GetPortions( nPar, aPortions ); + + USHORT nPCount = aPortions.Count(); + USHORT nStart = 0; + for ( USHORT nPos=0; nPos<nPCount; nPos++ ) + { + USHORT nEnd = aPortions.GetObject( nPos ); + ESelection aSel( nPar, nStart, nPar, nEnd ); + SfxItemSet aAttribs = rEngine.GetAttribs( aSel ); + + long nWestern = static_cast<const SvxFontHeightItem&>(aAttribs.Get(EE_CHAR_FONTHEIGHT)).GetHeight(); + long nCJK = static_cast<const SvxFontHeightItem&>(aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK)).GetHeight(); + long nCTL = static_cast<const SvxFontHeightItem&>(aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL)).GetHeight(); + + nWestern = ( nWestern * nPercent ) / 100; + nCJK = ( nCJK * nPercent ) / 100; + nCTL = ( nCTL * nPercent ) / 100; + + aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) ); + aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) ); + + rEngine.QuickSetAttribs( aAttribs, aSel ); //! remove paragraph attributes from aAttribs? + + nStart = nEnd; + } + } + + if ( bUpdateMode ) + rEngine.SetUpdateMode( TRUE ); +} + +long lcl_GetEditSize( EditEngine& rEngine, BOOL bWidth, BOOL bSwap, long nAttrRotate ) +{ + if ( bSwap ) + bWidth = !bWidth; + + if ( nAttrRotate ) + { + long nRealWidth = (long) rEngine.CalcTextWidth(); + long nRealHeight = rEngine.GetTextHeight(); + + // assuming standard mode, otherwise width isn't used + + double nRealOrient = nAttrRotate * F_PI18000; // 1/100th degrees + double nAbsCos = fabs( cos( nRealOrient ) ); + double nAbsSin = fabs( sin( nRealOrient ) ); + if ( bWidth ) + return (long) ( nRealWidth * nAbsCos + nRealHeight * nAbsSin ); + else + return (long) ( nRealHeight * nAbsCos + nRealWidth * nAbsSin ); + } + else if ( bWidth ) + return (long) rEngine.CalcTextWidth(); + else + return rEngine.GetTextHeight(); +} + + +void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const Rectangle& rAlignRect, + long nLeftM, long nTopM, long nRightM, long nBottomM, + BOOL bWidth, USHORT nOrient, long nAttrRotate, BOOL bPixelToLogic, + long& rEngineWidth, long& rEngineHeight, long& rNeededPixel, bool& rLeftClip, bool& rRightClip ) +{ + if ( !bWidth ) + { + // vertical + + long nScaleSize = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight; + + // Don't scale if it fits already. + // Allowing to extend into the margin, to avoid scaling at optimal height. + if ( nScaleSize <= rAlignRect.GetHeight() ) + return; + + BOOL bSwap = ( nOrient == SVX_ORIENTATION_TOPBOTTOM || nOrient == SVX_ORIENTATION_BOTTOMTOP ); + long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM; + long nScale = ( nAvailable * 100 ) / nScaleSize; + + lcl_ScaleFonts( rEngine, nScale ); + rEngineHeight = lcl_GetEditSize( rEngine, FALSE, bSwap, nAttrRotate ); + long nNewSize = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight; + + USHORT nShrinkAgain = 0; + while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) + { + // further reduce, like in DrawStrings + lcl_ScaleFonts( rEngine, 90 ); // reduce by 10% + rEngineHeight = lcl_GetEditSize( rEngine, FALSE, bSwap, nAttrRotate ); + nNewSize = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight; + ++nShrinkAgain; + } + + // sizes for further processing (alignment etc): + rEngineWidth = lcl_GetEditSize( rEngine, TRUE, bSwap, nAttrRotate ); + long nPixelWidth = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth; + rNeededPixel = nPixelWidth + nLeftM + nRightM; + } + else if ( rLeftClip || rRightClip ) + { + // horizontal + + long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM; + long nScaleSize = rNeededPixel - nLeftM - nRightM; // without margin + + if ( nScaleSize <= nAvailable ) + return; + + long nScale = ( nAvailable * 100 ) / nScaleSize; + + lcl_ScaleFonts( rEngine, nScale ); + rEngineWidth = lcl_GetEditSize( rEngine, TRUE, FALSE, nAttrRotate ); + long nNewSize = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth; + + USHORT nShrinkAgain = 0; + while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) + { + // further reduce, like in DrawStrings + lcl_ScaleFonts( rEngine, 90 ); // reduce by 10% + rEngineWidth = lcl_GetEditSize( rEngine, TRUE, FALSE, nAttrRotate ); + nNewSize = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth; + ++nShrinkAgain; + } + if ( nNewSize <= nAvailable ) + rLeftClip = rRightClip = FALSE; + + // sizes for further processing (alignment etc): + rNeededPixel = nNewSize + nLeftM + nRightM; + rEngineHeight = lcl_GetEditSize( rEngine, FALSE, FALSE, nAttrRotate ); + } +} + +void ScOutputData::DrawEdit(BOOL bPixelToLogic) +{ + vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() ); + + Size aMinSize = pRefDevice->PixelToLogic(Size(0,100)); // erst darueber wird ausgegeben +// UINT32 nMinHeight = aMinSize.Height() / 200; // 1/2 Pixel + + ScModule* pScMod = SC_MOD(); + sal_Int32 nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + // #105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed TRUE) + BOOL bCellContrast = bUseStyleColor && + Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + + ScFieldEditEngine* pEngine = NULL; + BOOL bHyphenatorSet = FALSE; + const ScPatternAttr* pOldPattern = NULL; + const SfxItemSet* pOldCondSet = NULL; + ScBaseCell* pCell = NULL; + + Size aRefOne = pRefDevice->PixelToLogic(Size(1,1)); + + long nInitPosX = nScrX; + if ( bLayoutRTL ) + { +#if 0 + Size aOnePixel = pDev->PixelToLogic(Size(1,1)); + long nOneX = aOnePixel.Width(); + nInitPosX += nMirrorW - nOneX; +#endif + nInitPosX += nMirrorW - 1; + } + long nLayoutSign = bLayoutRTL ? -1 : 1; + + //! store nLastContentCol as member! + SCCOL nLastContentCol = MAXCOL; + if ( nX2 < MAXCOL ) + nLastContentCol = sal::static_int_cast<SCCOL>( + nLastContentCol - pDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, MAXCOL, nY2, nTab, DIR_RIGHT ) ); + + long nRowPosY = nScrY; + for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 fuer Reste von zusammengefassten + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; +// long nCellHeight = (long) pThisRowInfo->nHeight; + if (nArrY==1) nRowPosY = nScrY; // vorher wird einzeln berechnet + + if ( pThisRowInfo->bChanged || nArrY==0 ) + { + long nPosX = 0; + for (SCCOL nX=0; nX<=nX2; nX++) // wegen Ueberhaengen + { + if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually + + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1]; + if (pInfo->bEditEngine) + { + SCROW nY = pThisRowInfo->nRowNo; + + SCCOL nCellX = nX; // position where the cell really starts + SCROW nCellY = nY; + BOOL bDoCell = FALSE; + + long nPosY = nRowPosY; + if ( nArrY == 0 ) + { + nPosY = nScrY; + nY = pRowInfo[1].nRowNo; + SCCOL nOverX; // start of the merged cells + SCROW nOverY; + if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, TRUE )) + { + nCellX = nOverX; + nCellY = nOverY; + bDoCell = TRUE; + } + } + else if ( nX == nX2 && !pThisRowInfo->pCellInfo[nX+1].pCell ) + { + // Rest of a long text further to the right? + + SCCOL nTempX=nX; + while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY )) + ++nTempX; + + if ( nTempX > nX && + !IsEmptyCellText( pThisRowInfo, nTempX, nY ) && + !pDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ) ) + { + nCellX = nTempX; + bDoCell = TRUE; + } + } + else + { + bDoCell = TRUE; + } + + if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow ) + bDoCell = FALSE; + + const ScPatternAttr* pPattern = NULL; + const SfxItemSet* pCondSet = NULL; + if (bDoCell) + { + if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 && + !pDoc->ColHidden(nCellX, nTab) ) + { + CellInfo& rCellInfo = pThisRowInfo->pCellInfo[nCellX+1]; + pPattern = rCellInfo.pPatternAttr; + pCondSet = rCellInfo.pConditionSet; + pCell = rCellInfo.pCell; + } + else // get from document + { + pPattern = pDoc->GetPattern( nCellX, nCellY, nTab ); + pCondSet = pDoc->GetCondResult( nCellX, nCellY, nTab ); + GetVisibleCell( nCellX, nCellY, nTab, pCell ); + } + if ( !pCell ) + bDoCell = FALSE; + } + if (bDoCell) + { + BOOL bHidden = FALSE; + + // + // Create EditEngine + // + + if (!pEngine) + { + // Ein RefDevice muss auf jeden Fall gesetzt werden, + // sonst legt sich die EditEngine ein VirtualDevice an! + pEngine = new ScFieldEditEngine( pDoc->GetEnginePool() ); + pEngine->SetUpdateMode( FALSE ); + pEngine->SetRefDevice( pFmtDevice ); // always set + ULONG nCtrl = pEngine->GetControlWord(); + if ( bShowSpellErrors ) + nCtrl |= EE_CNTRL_ONLINESPELLING; + if ( eType == OUTTYPE_PRINTER ) + nCtrl &= ~EE_CNTRL_MARKFIELDS; + pEngine->SetControlWord( nCtrl ); + pEngine->SetForbiddenCharsTable( pDoc->GetForbiddenCharacters() ); + pEngine->SetAsianCompressionMode( pDoc->GetAsianCompression() ); + pEngine->SetKernAsianPunctuation( pDoc->GetAsianKerning() ); + pEngine->EnableAutoColor( bUseStyleColor ); + pEngine->SetDefaultHorizontalTextDirection( + (EEHorizontalTextDirection)pDoc->GetEditTextDirection( nTab ) ); + } + else + lcl_ClearEdit( *pEngine ); // also calls SetUpdateMode(FALSE) + + + BOOL bCellIsValue = lcl_SafeIsValue(pCell); + + SvxCellHorJustify eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&) + pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet)).GetValue(); + BOOL bBreak = ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) || + ((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK, pCondSet)).GetValue(); + BOOL bRepeat = ( eHorJust == SVX_HOR_JUSTIFY_REPEAT && !bBreak ); + BOOL bShrink = !bBreak && !bRepeat && static_cast<const SfxBoolItem&> + (pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue(); + SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet ); + long nAttrRotate = ((const SfxInt32Item&)pPattern-> + GetItem(ATTR_ROTATE_VALUE, pCondSet)).GetValue(); + if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT ) + { + // ignore orientation/rotation if "repeat" is active + eOrient = SVX_ORIENTATION_STANDARD; + nAttrRotate = 0; + + // #i31843# "repeat" with "line breaks" is treated as default alignment + // (but rotation is still disabled) + if ( bBreak ) + eHorJust = SVX_HOR_JUSTIFY_STANDARD; + } + if ( eOrient==SVX_ORIENTATION_STANDARD && nAttrRotate ) + { + //! Flag setzen, um die Zelle in DrawRotated wiederzufinden ? + //! (oder Flag schon bei DrawBackground, dann hier keine Abfrage) + bHidden = TRUE; // gedreht wird getrennt ausgegeben + } + + BOOL bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED && + ((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() ); + if ( bAsianVertical ) + { + // in asian mode, use EditEngine::SetVertical instead of EE_CNTRL_ONECHARPERLINE + eOrient = SVX_ORIENTATION_STANDARD; + // default alignment for asian vertical mode is top-right + if ( eHorJust == SVX_HOR_JUSTIFY_STANDARD ) + eHorJust = SVX_HOR_JUSTIFY_RIGHT; + } + + + + SvxCellHorJustify eOutHorJust = + ( eHorJust != SVX_HOR_JUSTIFY_STANDARD ) ? eHorJust : + ( bCellIsValue ? SVX_HOR_JUSTIFY_RIGHT : SVX_HOR_JUSTIFY_LEFT ); + + if ( eOutHorJust == SVX_HOR_JUSTIFY_BLOCK || eOutHorJust == SVX_HOR_JUSTIFY_REPEAT ) + eOutHorJust = SVX_HOR_JUSTIFY_LEFT; // repeat is not yet implemented + + +//! if ( !bHidden && eType == OUTTYPE_PRINTER && +//! pDev->GetOutDevType() == OUTDEV_WINDOW && +//! ((const SvxFontHeightItem&)pPattern-> +//! GetItem(ATTR_FONT_HEIGHT)).GetHeight() <= nMinHeight ) +//! { +//! Point aPos( nStartX, nStartY ); +//! pDev->DrawPixel( aPos, +//! ((const SvxColorItem&)pPattern-> +//! GetItem( ATTR_FONT_COLOR )).GetValue() ); +//! bHidden = TRUE; +//! } + + if (!bHidden) + { + //! mirror margin values for RTL? + //! move margin down to after final GetOutputArea call + + const SvxMarginItem* pMargin = (const SvxMarginItem*) + &pPattern->GetItem(ATTR_MARGIN, pCondSet); + USHORT nIndent = 0; + if ( eHorJust == SVX_HOR_JUSTIFY_LEFT ) + nIndent = ((const SfxUInt16Item&)pPattern-> + GetItem(ATTR_INDENT, pCondSet)).GetValue(); + + long nLeftM = (long) ( (pMargin->GetLeftMargin() + nIndent) * nPPTX ); + long nTopM = (long) ( pMargin->GetTopMargin() * nPPTY ); + long nRightM = (long) ( pMargin->GetRightMargin() * nPPTX ); + long nBottomM = (long) ( pMargin->GetBottomMargin() * nPPTY ); + + SCCOL nXForPos = nX; + if ( nXForPos < nX1 ) + { + nXForPos = nX1; + nPosX = nInitPosX; + } + SCSIZE nArrYForPos = nArrY; + if ( nArrYForPos < 1 ) + { + nArrYForPos = 1; + nPosY = nScrY; + } + + OutputAreaParam aAreaParam; + + // + // Initial page size - large for normal text, cell size for automatic line breaks + // + + Size aPaperSize = Size( 1000000, 1000000 ); + if ( bBreak || eOrient == SVX_ORIENTATION_STACKED || bAsianVertical ) + { + //! also stacked, AsianVertical + + // call GetOutputArea with nNeeded=0, to get only the cell width + + //! handle nArrY == 0 + GetOutputArea( nXForPos, nArrYForPos, nPosX, nPosY, nCellX, nCellY, 0, + *pPattern, sal::static_int_cast<USHORT>(eOutHorJust), + bCellIsValue, true, false, aAreaParam ); + + //! special ScEditUtil handling if formatting for printer + + if ( eOrient == SVX_ORIENTATION_TOPBOTTOM || eOrient == SVX_ORIENTATION_BOTTOMTOP ) + aPaperSize.Width() = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; + else + aPaperSize.Width() = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM; + + if (bAsianVertical && bBreak) + { + // add some extra height (default margin value) for safety + // as long as GetEditArea isn't used below + long nExtraHeight = (long)( 20 * nPPTY ); + aPaperSize.Height() = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM + nExtraHeight; + } + } + if (bPixelToLogic) + { + Size aLogicSize = pRefDevice->PixelToLogic(aPaperSize); + if ( bBreak && !bAsianVertical && pRefDevice != pFmtDevice ) + { + // #i85342# screen display and formatting for printer, + // use same GetEditArea call as in ScViewData::SetEditEngine + + Fraction aFract(1,1); + Rectangle aUtilRect = ScEditUtil( pDoc, nCellX, nCellY, nTab, Point(0,0), pFmtDevice, + HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( pPattern, FALSE ); + aLogicSize.Width() = aUtilRect.GetWidth(); + } + pEngine->SetPaperSize(aLogicSize); + } + else + pEngine->SetPaperSize(aPaperSize); + + // + // Fill the EditEngine (cell attributes and text) + // + + SvxCellVerJustify eVerJust = (SvxCellVerJustify)((const SvxVerJustifyItem&) + pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet)).GetValue(); + + // default alignment for asian vertical mode is top-right + if ( bAsianVertical && eVerJust == SVX_VER_JUSTIFY_STANDARD ) + eVerJust = SVX_VER_JUSTIFY_TOP; + + // syntax highlighting mode is ignored here + // StringDiffer doesn't look at hyphenate, language items + if ( pPattern != pOldPattern || pCondSet != pOldCondSet ) + { + SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() ); + pPattern->FillEditItemSet( pSet, pCondSet ); + + pEngine->SetDefaults( pSet ); + pOldPattern = pPattern; + pOldCondSet = pCondSet; + + ULONG nControl = pEngine->GetControlWord(); + if (eOrient==SVX_ORIENTATION_STACKED) + nControl |= EE_CNTRL_ONECHARPERLINE; + else + nControl &= ~EE_CNTRL_ONECHARPERLINE; + pEngine->SetControlWord( nControl ); + + if ( !bHyphenatorSet && ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) + { + // set hyphenator the first time it is needed + com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); + pEngine->SetHyphenator( xXHyphenator ); + bHyphenatorSet = TRUE; + } + + Color aBackCol = ((const SvxBrushItem&) + pPattern->GetItem( ATTR_BACKGROUND, pCondSet )).GetColor(); + if ( bUseStyleColor && ( aBackCol.GetTransparency() > 0 || bCellContrast ) ) + aBackCol.SetColor( nConfBackColor ); + pEngine->SetBackgroundColor( aBackCol ); + } + + // horizontal alignment now may depend on cell content + // (for values with number formats with mixed script types) + // -> always set adjustment + + SvxAdjust eSvxAdjust = SVX_ADJUST_LEFT; + if (eOrient==SVX_ORIENTATION_STACKED) + eSvxAdjust = SVX_ADJUST_CENTER; + else if (bBreak) + { + if (eOrient==SVX_ORIENTATION_STANDARD && !bAsianVertical) + switch (eHorJust) + { + case SVX_HOR_JUSTIFY_STANDARD: + eSvxAdjust = bCellIsValue ? SVX_ADJUST_RIGHT : SVX_ADJUST_LEFT; + break; + case SVX_HOR_JUSTIFY_LEFT: + case SVX_HOR_JUSTIFY_REPEAT: // nicht implementiert + eSvxAdjust = SVX_ADJUST_LEFT; + break; + case SVX_HOR_JUSTIFY_RIGHT: + eSvxAdjust = SVX_ADJUST_RIGHT; + break; + case SVX_HOR_JUSTIFY_CENTER: + eSvxAdjust = SVX_ADJUST_CENTER; + break; + case SVX_HOR_JUSTIFY_BLOCK: + eSvxAdjust = SVX_ADJUST_BLOCK; + break; + } + else + switch (eVerJust) + { + case SVX_VER_JUSTIFY_TOP: + eSvxAdjust = (eOrient==SVX_ORIENTATION_TOPBOTTOM || bAsianVertical) ? + SVX_ADJUST_LEFT : SVX_ADJUST_RIGHT; + break; + case SVX_VER_JUSTIFY_CENTER: + eSvxAdjust = SVX_ADJUST_CENTER; + break; + case SVX_VER_JUSTIFY_BOTTOM: + case SVX_HOR_JUSTIFY_STANDARD: + eSvxAdjust = (eOrient==SVX_ORIENTATION_TOPBOTTOM || bAsianVertical) ? + SVX_ADJUST_RIGHT : SVX_ADJUST_LEFT; + break; + } + } + pEngine->SetDefaultItem( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); + + // Read content from cell + + BOOL bWrapFields = FALSE; + if (pCell) + { + if (pCell->GetCellType() == CELLTYPE_EDIT) + { + const EditTextObject* pData; + ((ScEditCell*)pCell)->GetData(pData); + + if (pData) + { + pEngine->SetText(*pData); + + if ( bBreak && !bAsianVertical && pData->HasField() ) + { + // Fields aren't wrapped, so clipping is enabled to prevent + // a field from being drawn beyond the cell size + + bWrapFields = TRUE; + } + } + else + { + DBG_ERROR("pData == 0"); + } + } + else + { + ULONG nFormat = pPattern->GetNumberFormat( + pDoc->GetFormatTable(), pCondSet ); + String aString; + Color* pColor; + ScCellFormat::GetString( pCell, + nFormat,aString, &pColor, + *pDoc->GetFormatTable(), + bShowNullValues, + bShowFormulas, + ftCheck ); + + pEngine->SetText(aString); + if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) ) + lcl_SetEditColor( *pEngine, *pColor ); + } + + if ( bSyntaxMode ) + SetEditSyntaxColor( *pEngine, pCell ); + else if ( bUseStyleColor && bForceAutoColor ) + lcl_SetEditColor( *pEngine, COL_AUTO ); //! or have a flag at EditEngine + } + else + { + DBG_ERROR("pCell == NULL"); + } + + pEngine->SetVertical( bAsianVertical ); + pEngine->SetUpdateMode( TRUE ); // after SetText, before CalcTextWidth/GetTextHeight + + // + // Get final output area using the calculated width + // + + long nEngineWidth; + if ( bBreak && eOrient != SVX_ORIENTATION_STACKED && !bAsianVertical ) + nEngineWidth = 0; + else + nEngineWidth = (long) pEngine->CalcTextWidth(); + long nEngineHeight = pEngine->GetTextHeight(); + + if (eOrient != SVX_ORIENTATION_STANDARD && + eOrient != SVX_ORIENTATION_STACKED) + { + long nTemp = nEngineWidth; + nEngineWidth = nEngineHeight; + nEngineHeight = nTemp; + } + + if (eOrient == SVX_ORIENTATION_STACKED) + nEngineWidth = nEngineWidth * 11 / 10; + + long nNeededPixel = nEngineWidth; + if (bPixelToLogic) + nNeededPixel = pRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width(); + nNeededPixel += nLeftM + nRightM; + + if ( ( !bBreak && eOrient != SVX_ORIENTATION_STACKED ) || bAsianVertical || bShrink ) + { + // for break, the first GetOutputArea call is sufficient + GetOutputArea( nXForPos, nArrYForPos, nPosX, nPosY, nCellX, nCellY, nNeededPixel, + *pPattern, sal::static_int_cast<USHORT>(eOutHorJust), + bCellIsValue || bRepeat || bShrink, false, false, aAreaParam ); + + if ( bShrink ) + { + BOOL bWidth = ( eOrient == SVX_ORIENTATION_STANDARD && !bAsianVertical ); + ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, + nLeftM, nTopM, nRightM, nBottomM, bWidth, + sal::static_int_cast<USHORT>(eOrient), 0, bPixelToLogic, + nEngineWidth, nEngineHeight, nNeededPixel, + aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); + } + + if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && pEngine->GetParagraphCount() == 1 ) + { + // First check if twice the space for the formatted text is available + // (otherwise just keep it unchanged). + + long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin + long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM; + if ( nAvailable >= 2 * nFormatted ) + { + // "repeat" is handled with unformatted text (for performance reasons) + String aCellStr = pEngine->GetText(); + pEngine->SetText( aCellStr ); + + long nRepeatSize = (long) pEngine->CalcTextWidth(); + if (bPixelToLogic) + nRepeatSize = pRefDevice->LogicToPixel(Size(nRepeatSize,0)).Width(); + if ( pFmtDevice != pRefDevice ) + ++nRepeatSize; + if ( nRepeatSize > 0 ) + { + long nRepeatCount = nAvailable / nRepeatSize; + if ( nRepeatCount > 1 ) + { + String aRepeated = aCellStr; + for ( long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) + aRepeated.Append( aCellStr ); + pEngine->SetText( aRepeated ); + + nEngineHeight = pEngine->GetTextHeight(); + nEngineWidth = (long) pEngine->CalcTextWidth(); + if (bPixelToLogic) + nNeededPixel = pRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width(); + else + nNeededPixel = nEngineWidth; + nNeededPixel += nLeftM + nRightM; + } + } + } + } + + if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) + { + pEngine->SetText( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("###")) ); + nEngineWidth = (long) pEngine->CalcTextWidth(); + if (bPixelToLogic) + nNeededPixel = pRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width(); + else + nNeededPixel = nEngineWidth; + nNeededPixel += nLeftM + nRightM; + + // No clip marks if "###" doesn't fit (same as in DrawStrings) + } + + if ( eOutHorJust != SVX_HOR_JUSTIFY_LEFT && eOrient == SVX_ORIENTATION_STANDARD ) + { + aPaperSize.Width() = nNeededPixel + 1; + if (bPixelToLogic) + pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize)); + else + pEngine->SetPaperSize(aPaperSize); + } + } + + long nStartX = aAreaParam.maAlignRect.Left(); + long nStartY = aAreaParam.maAlignRect.Top(); + long nCellWidth = aAreaParam.maAlignRect.GetWidth(); + long nOutWidth = nCellWidth - 1 - nLeftM - nRightM; + long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; + + if ( bBreak || eOrient != SVX_ORIENTATION_STANDARD || bAsianVertical ) + { + // text with automatic breaks is aligned only within the + // edit engine's paper size, the output of the whole area + // is always left-aligned + + nStartX += nLeftM; + } + else + { + if ( eOutHorJust == SVX_HOR_JUSTIFY_RIGHT ) + nStartX -= nNeededPixel - nCellWidth + nRightM + 1; + else if ( eOutHorJust == SVX_HOR_JUSTIFY_CENTER ) + nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2; + else + nStartX += nLeftM; + } + + BOOL bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW ); + if ( aAreaParam.maClipRect.Left() < nScrX ) + { + aAreaParam.maClipRect.Left() = nScrX; + aAreaParam.mbLeftClip = true; + } + if ( aAreaParam.maClipRect.Right() > nScrX + nScrW ) + { + aAreaParam.maClipRect.Right() = nScrX + nScrW; //! minus one? + aAreaParam.mbRightClip = true; + } + + if ( !bHidden && !bOutside ) + { + bool bClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip; + BOOL bSimClip = FALSE; + + if ( bWrapFields ) + { + // Fields in a cell with automatic breaks: clip to cell width + bClip = TRUE; + } + + if ( aAreaParam.maClipRect.Top() < nScrY ) + { + aAreaParam.maClipRect.Top() = nScrY; + bClip = TRUE; + } + if ( aAreaParam.maClipRect.Bottom() > nScrY + nScrH ) + { + aAreaParam.maClipRect.Bottom() = nScrY + nScrH; //! minus one? + bClip = TRUE; + } + + Size aCellSize; // output area, excluding margins, in logical units + if (bPixelToLogic) + aCellSize = pRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ); + else + aCellSize = Size( nOutWidth, nOutHeight ); + + if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() ) + { + const ScMergeAttr* pMerge = + (ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); + BOOL bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; + + // Don't clip for text height when printing rows with optimal height, + // except when font size is from conditional formatting. + //! Allow clipping when vertically merged? + if ( eType != OUTTYPE_PRINTER || + ( pDoc->GetRowFlags( nCellY, nTab ) & CR_MANUALSIZE ) || + ( pCondSet && SFX_ITEM_SET == + pCondSet->GetItemState(ATTR_FONT_HEIGHT, TRUE) ) ) + bClip = TRUE; + else + bSimClip = TRUE; + + // Show clip marks if height is at least 5pt too small and + // there are several lines of text. + // Not for asian vertical text, because that would interfere + // with the default right position of the text. + // Only with automatic line breaks, to avoid having to find + // the cells with the horizontal end of the text again. + if ( nEngineHeight - aCellSize.Height() > 100 && + ( bBreak || eOrient == SVX_ORIENTATION_STACKED ) && + !bAsianVertical && bMarkClipped && + ( pEngine->GetParagraphCount() > 1 || pEngine->GetLineCount(0) > 1 ) ) + { + CellInfo* pClipMarkCell = NULL; + if ( bMerged ) + { + // anywhere in the merged area... + SCCOL nClipX = ( nX < nX1 ) ? nX1 : nX; + pClipMarkCell = &pRowInfo[(nArrY != 0) ? nArrY : 1].pCellInfo[nClipX+1]; + } + else + pClipMarkCell = &pThisRowInfo->pCellInfo[nX+1]; + + pClipMarkCell->nClipMark |= SC_CLIPMARK_RIGHT; //! also allow left? + bAnyClipped = TRUE; + + long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX ); + if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() ) + aAreaParam.maClipRect.Right() -= nMarkPixel; + } + } + +#if 0 + long nClipStartY = nStartY; + if (nArrY==0 || bVisChanged) + { + if ( nClipStartY < nRowPosY ) + { + long nDif = nRowPosY - nClipStartY; + bClip = TRUE; + nClipStartY = nRowPosY; + aClipSize.Height() -= nDif; + } + } +#endif + + Rectangle aLogicClip; + if (bClip || bSimClip) + { + // Clip marks are already handled in GetOutputArea + + if (bPixelToLogic) + aLogicClip = pRefDevice->PixelToLogic( aAreaParam.maClipRect ); + else + aLogicClip = aAreaParam.maClipRect; + + if (bClip) // bei bSimClip nur aClipRect initialisieren + { + if (bMetaFile) + { + pDev->Push(); + pDev->IntersectClipRegion( aLogicClip ); + } + else + pDev->SetClipRegion( Region( aLogicClip ) ); + } + } + + Point aLogicStart; + if (bPixelToLogic) + aLogicStart = pRefDevice->PixelToLogic( Point(nStartX,nStartY) ); + else + aLogicStart = Point(nStartX, nStartY); + if ( eOrient!=SVX_ORIENTATION_STANDARD || bAsianVertical || !bBreak ) + { + long nAvailWidth = aCellSize.Width(); + // space for AutoFilter is already handled in GetOutputArea + + // horizontal alignment + + if (eOrient==SVX_ORIENTATION_STANDARD && !bAsianVertical) + { + if (eHorJust==SVX_HOR_JUSTIFY_RIGHT || + eHorJust==SVX_HOR_JUSTIFY_CENTER || + (eHorJust==SVX_HOR_JUSTIFY_STANDARD && bCellIsValue) ) + { + pEngine->SetUpdateMode( FALSE ); + + SvxAdjust eEditAdjust = + (eHorJust==SVX_HOR_JUSTIFY_CENTER) ? + SVX_ADJUST_CENTER : SVX_ADJUST_RIGHT; + pEngine->SetDefaultItem( + SvxAdjustItem( eEditAdjust, EE_PARA_JUST ) ); + + // #55142# reset adjustment for the next cell + pOldPattern = NULL; + + pEngine->SetUpdateMode( TRUE ); + } + } + else + { + if (eHorJust==SVX_HOR_JUSTIFY_RIGHT) + aLogicStart.X() += nAvailWidth - nEngineWidth; + else if (eHorJust==SVX_HOR_JUSTIFY_CENTER) + aLogicStart.X() += (nAvailWidth - nEngineWidth) / 2; + } + } + + if ( bAsianVertical ) + { + // paper size is subtracted below + aLogicStart.X() += nEngineWidth; + } + + if ( ( bAsianVertical || eOrient == SVX_ORIENTATION_TOPBOTTOM || + eOrient == SVX_ORIENTATION_BOTTOMTOP ) && bBreak ) + { + // vertical adjustment is within the EditEngine + if (bPixelToLogic) + aLogicStart.Y() += pRefDevice->PixelToLogic(Size(0,nTopM)).Height(); + else + aLogicStart.Y() += nTopM; + } + + if ( ( eOrient==SVX_ORIENTATION_STANDARD && !bAsianVertical ) || + eOrient==SVX_ORIENTATION_STACKED || !bBreak ) + { + if (eVerJust==SVX_VER_JUSTIFY_BOTTOM || + eVerJust==SVX_VER_JUSTIFY_STANDARD) + { + //! if pRefDevice != pFmtDevice, keep heights in logic units, + //! only converting margin? + + if (bPixelToLogic) + aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0, nTopM + + pRefDevice->LogicToPixel(aCellSize).Height() - + pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() + )).Height(); + else + aLogicStart.Y() += nTopM + aCellSize.Height() - nEngineHeight; + } + else if (eVerJust==SVX_VER_JUSTIFY_CENTER) + { + if (bPixelToLogic) + aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0, nTopM + ( + pRefDevice->LogicToPixel(aCellSize).Height() - + pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() ) + / 2)).Height(); + else + aLogicStart.Y() += nTopM + (aCellSize.Height() - nEngineHeight) / 2; + } + else // top + { + if (bPixelToLogic) + aLogicStart.Y() += pRefDevice->PixelToLogic(Size(0,nTopM)).Height(); + else + aLogicStart.Y() += nTopM; + } + } + + Point aURLStart = aLogicStart; // copy before modifying for orientation + + short nOriVal = 0; + if (eOrient==SVX_ORIENTATION_TOPBOTTOM) + { + // nOriVal = -900; + nOriVal = 2700; + aLogicStart.X() += nEngineWidth; + } + else if (eOrient==SVX_ORIENTATION_BOTTOMTOP) + { + nOriVal = 900; + aLogicStart.Y() += bBreak ? pEngine->GetPaperSize().Width() : + nEngineHeight; + } + else if (eOrient==SVX_ORIENTATION_STACKED) + { + Size aPaperLogic = pEngine->GetPaperSize(); + aPaperLogic.Width() = nEngineWidth; + pEngine->SetPaperSize(aPaperLogic); + } + + if ( pEngine->IsRightToLeft( 0 ) ) + { + // For right-to-left, EditEngine always calculates its lines + // beginning from the right edge, but EditLine::nStartPosX is + // of USHORT type, so the PaperSize must be limited to USHRT_MAX. + Size aLogicPaper = pEngine->GetPaperSize(); + if ( aLogicPaper.Width() > USHRT_MAX ) + { + aLogicPaper.Width() = USHRT_MAX; + pEngine->SetPaperSize(aLogicPaper); + } + } + + // bMoveClipped handling has been replaced by complete alignment + // handling (also extending to the left). + + if ( bSimClip && !nOriVal && !bAsianVertical ) + { + // kein hartes Clipping, aber nur die betroffenen + // Zeilen ausgeben + + Point aDocStart = aLogicClip.TopLeft(); + aDocStart -= aLogicStart; + pEngine->Draw( pDev, aLogicClip, aDocStart, FALSE ); + } + else + { + if (bAsianVertical) + { + // with SetVertical, the start position is top left of + // the whole output area, not the text itself + aLogicStart.X() -= pEngine->GetPaperSize().Width(); + } + pEngine->Draw( pDev, aLogicStart, nOriVal ); + } + + if (bClip) + { + if (bMetaFile) + pDev->Pop(); + else + pDev->SetClipRegion(); + } + + // PDF: whole-cell hyperlink from formula? + BOOL bHasURL = pPDFData && pCell && pCell->GetCellType() == CELLTYPE_FORMULA && + static_cast<ScFormulaCell*>(pCell)->IsHyperLinkCell(); + if ( bHasURL ) + { + long nURLWidth = (long) pEngine->CalcTextWidth(); + long nURLHeight = pEngine->GetTextHeight(); + if ( bBreak ) + { + Size aPaper = pEngine->GetPaperSize(); + if ( bAsianVertical ) + nURLHeight = aPaper.Height(); + else + nURLWidth = aPaper.Width(); + } + if ( eOrient == SVX_ORIENTATION_TOPBOTTOM || eOrient == SVX_ORIENTATION_BOTTOMTOP ) + std::swap( nURLWidth, nURLHeight ); + else if ( bAsianVertical ) + aURLStart.X() -= nURLWidth; + + Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) ); + lcl_DoHyperlinkResult( pDev, aURLRect, pCell ); + } + } + } + } + } + nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign; + } + } + nRowPosY += pRowInfo[nArrY].nHeight; + } + + delete pEngine; + + if (bAnyRotated) + DrawRotated(bPixelToLogic); //! von aussen rufen ? +} + +// ------------------------------------------------------------------------------- + +void ScOutputData::DrawRotated(BOOL bPixelToLogic) +{ + //! 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; + + + ScModule* pScMod = SC_MOD(); + sal_Int32 nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + // #105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed TRUE) + BOOL bCellContrast = bUseStyleColor && + Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + + ScFieldEditEngine* pEngine = NULL; + BOOL bHyphenatorSet = FALSE; + const ScPatternAttr* pPattern; + const SfxItemSet* pCondSet; + const ScPatternAttr* pOldPattern = NULL; + const SfxItemSet* pOldCondSet = NULL; + ScBaseCell* pCell = NULL; + + long nInitPosX = nScrX; + if ( bLayoutRTL ) + { +#if 0 + Size aOnePixel = pDev->PixelToLogic(Size(1,1)); + long nOneX = aOnePixel.Width(); + nInitPosX += nMirrorW - nOneX; +#endif + nInitPosX += nMirrorW - 1; + } + long nLayoutSign = bLayoutRTL ? -1 : 1; + + long nRowPosY = nScrY; + for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 fuer Reste von zusammengefassten + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + long nCellHeight = (long) pThisRowInfo->nHeight; + if (nArrY==1) nRowPosY = nScrY; // vorher wird einzeln berechnet + + if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE ) + { + long nPosX = 0; + for (SCCOL nX=0; nX<=nRotMax; nX++) + { + if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually + + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1]; + if ( pInfo->nRotateDir != SC_ROTDIR_NONE ) + { + SCROW nY = pThisRowInfo->nRowNo; + + BOOL bHidden = FALSE; + if (bEditMode) + if ( nX == nEditCol && nY == nEditRow ) + bHidden = TRUE; + + if (!bHidden) + { + if (!pEngine) + { + // Ein RefDevice muss auf jeden Fall gesetzt werden, + // sonst legt sich die EditEngine ein VirtualDevice an! + pEngine = new ScFieldEditEngine( pDoc->GetEnginePool() ); + pEngine->SetUpdateMode( FALSE ); + pEngine->SetRefDevice( pFmtDevice ); // always set + ULONG nCtrl = pEngine->GetControlWord(); + if ( bShowSpellErrors ) + nCtrl |= EE_CNTRL_ONLINESPELLING; + if ( eType == OUTTYPE_PRINTER ) + nCtrl &= ~EE_CNTRL_MARKFIELDS; + pEngine->SetControlWord( nCtrl ); + pEngine->SetForbiddenCharsTable( pDoc->GetForbiddenCharacters() ); + pEngine->SetAsianCompressionMode( pDoc->GetAsianCompression() ); + pEngine->SetKernAsianPunctuation( pDoc->GetAsianKerning() ); + pEngine->EnableAutoColor( bUseStyleColor ); + pEngine->SetDefaultHorizontalTextDirection( + (EEHorizontalTextDirection)pDoc->GetEditTextDirection( nTab ) ); + } + else + lcl_ClearEdit( *pEngine ); // also calls SetUpdateMode(FALSE) + + long nPosY = nRowPosY; + BOOL bVisChanged = FALSE; + + //! Rest von zusammengefasster Zelle weiter oben funktioniert nicht! + + BOOL bFromDoc = FALSE; + pPattern = pInfo->pPatternAttr; + pCondSet = pInfo->pConditionSet; + if (!pPattern) + { + pPattern = pDoc->GetPattern( nX, nY, nTab ); + bFromDoc = TRUE; + } + pCell = pInfo->pCell; + if (bFromDoc) + pCondSet = pDoc->GetCondResult( nX, nY, nTab ); + + if (!pCell && nX>nX2) + GetVisibleCell( nX, nY, nTab, pCell ); + + if ( !pCell || IsEmptyCellText( pThisRowInfo, nX, nY ) ) + bHidden = TRUE; // nRotateDir is also set without a cell + + long nCellWidth = (long) pRowInfo[0].pCellInfo[nX+1].nWidth; + + SvxCellHorJustify eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&) + pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet)).GetValue(); + BOOL bBreak = ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) || + ((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK, pCondSet)).GetValue(); + BOOL bRepeat = ( eHorJust == SVX_HOR_JUSTIFY_REPEAT && !bBreak ); + BOOL bShrink = !bBreak && !bRepeat && static_cast<const SfxBoolItem&> + (pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue(); + SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet ); + + const ScMergeAttr* pMerge = + (ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); + BOOL bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; + + long nStartX = nPosX; + long nStartY = nPosY; + if (nX<nX1) + { + if ((bBreak || eOrient!=SVX_ORIENTATION_STANDARD) && !bMerged) + bHidden = TRUE; + else + { + nStartX = nInitPosX; + SCCOL nCol = nX1; + while (nCol > nX) + { + --nCol; + nStartX -= nLayoutSign * (long) pRowInfo[0].pCellInfo[nCol+1].nWidth; + } + } + } + long nCellStartX = nStartX; + + // Ersatzdarstellung fuer zu kleinen Text weggelassen + + if (!bHidden) + { + long nOutWidth = nCellWidth - 1; + long nOutHeight; + if (pInfo) + nOutHeight = nCellHeight; + else + nOutHeight = (long) ( pDoc->GetRowHeight(nY,nTab) * nPPTY ); + + if ( bMerged ) // Zusammengefasst + { + SCCOL nCountX = pMerge->GetColMerge(); + for (SCCOL i=1; i<nCountX; i++) + nOutWidth += (long) ( pDoc->GetColWidth(nX+i,nTab) * nPPTX ); + SCROW nCountY = pMerge->GetRowMerge(); + nOutHeight += (long) pDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, nTab, nPPTY); + } + + SvxCellVerJustify eVerJust = (SvxCellVerJustify)((const SvxVerJustifyItem&) + pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet)).GetValue(); + + // Syntax-Modus wird hier ignoriert... + + // StringDiffer doesn't look at hyphenate, language items + if ( pPattern != pOldPattern || pCondSet != pOldCondSet ) + { + SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() ); + pPattern->FillEditItemSet( pSet, pCondSet ); + + // Ausrichtung fuer EditEngine + SvxAdjust eSvxAdjust = SVX_ADJUST_LEFT; + if (eOrient==SVX_ORIENTATION_STACKED) + eSvxAdjust = SVX_ADJUST_CENTER; + // Adjustment fuer bBreak ist hier weggelassen + pSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); + + pEngine->SetDefaults( pSet ); + pOldPattern = pPattern; + pOldCondSet = pCondSet; + + ULONG nControl = pEngine->GetControlWord(); + if (eOrient==SVX_ORIENTATION_STACKED) + nControl |= EE_CNTRL_ONECHARPERLINE; + else + nControl &= ~EE_CNTRL_ONECHARPERLINE; + pEngine->SetControlWord( nControl ); + + if ( !bHyphenatorSet && ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) + { + // set hyphenator the first time it is needed + com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); + pEngine->SetHyphenator( xXHyphenator ); + bHyphenatorSet = TRUE; + } + + Color aBackCol = ((const SvxBrushItem&) + pPattern->GetItem( ATTR_BACKGROUND, pCondSet )).GetColor(); + if ( bUseStyleColor && ( aBackCol.GetTransparency() > 0 || bCellContrast ) ) + aBackCol.SetColor( nConfBackColor ); + pEngine->SetBackgroundColor( aBackCol ); + } + + // Raender + + //! Position und Papersize auf EditUtil umstellen !!! + + const SvxMarginItem* pMargin = (const SvxMarginItem*) + &pPattern->GetItem(ATTR_MARGIN, pCondSet); + USHORT nIndent = 0; + if ( eHorJust == SVX_HOR_JUSTIFY_LEFT ) + nIndent = ((const SfxUInt16Item&)pPattern-> + GetItem(ATTR_INDENT, pCondSet)).GetValue(); + + long nTotalHeight = nOutHeight; // ohne Rand abzuziehen + if ( bPixelToLogic ) + nTotalHeight = pRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height(); + + long nLeftM = (long) ( (pMargin->GetLeftMargin() + nIndent) * nPPTX ); + long nTopM = (long) ( pMargin->GetTopMargin() * nPPTY ); + long nRightM = (long) ( pMargin->GetRightMargin() * nPPTX ); + long nBottomM = (long) ( pMargin->GetBottomMargin() * nPPTY ); + nStartX += nLeftM; + nStartY += nTopM; + nOutWidth -= nLeftM + nRightM; + nOutHeight -= nTopM + nBottomM; + + // Rotation schon hier, um bei Umbruch auch PaperSize anzupassen + long nAttrRotate = 0; + double nSin = 0.0; + double nCos = 1.0; + SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD; + if ( eOrient == SVX_ORIENTATION_STANDARD ) + { + nAttrRotate = ((const SfxInt32Item&)pPattern-> + GetItem(ATTR_ROTATE_VALUE, pCondSet)).GetValue(); + if ( nAttrRotate ) + { + eRotMode = (SvxRotateMode)((const SvxRotateModeItem&) + pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet)).GetValue(); + + if ( nAttrRotate == 18000 ) + eRotMode = SVX_ROTATE_MODE_STANDARD; // keinen Ueberlauf + + if ( bLayoutRTL ) + nAttrRotate = -nAttrRotate; + + double nRealOrient = nAttrRotate * F_PI18000; // 1/100 Grad + nCos = cos( nRealOrient ); + nSin = sin( nRealOrient ); + } + } + + Size aPaperSize = Size( 1000000, 1000000 ); + if (eOrient==SVX_ORIENTATION_STACKED) + aPaperSize.Width() = nOutWidth; // zum Zentrieren + else if (bBreak) + { + if (nAttrRotate) + { + //! richtige PaperSize fuer Umbruch haengt von der Zeilenzahl + //! ab, solange die Zeilen nicht einzeln versetzt ausgegeben + //! werden koennen -> darum unbegrenzt, also kein Umbruch. + //! Mit versetzten Zeilen waere das folgende richtig: + aPaperSize.Width() = (long)(nOutHeight / fabs(nSin)); + } + else if (eOrient == SVX_ORIENTATION_STANDARD) + aPaperSize.Width() = nOutWidth; + else + aPaperSize.Width() = nOutHeight - 1; + } + if (bPixelToLogic) + pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize)); + else + pEngine->SetPaperSize(aPaperSize); // Scale ist immer 1 + + // Daten aus Zelle lesen + + if (pCell) + { + if (pCell->GetCellType() == CELLTYPE_EDIT) + { + const EditTextObject* pData; + ((ScEditCell*)pCell)->GetData(pData); + + if (pData) + pEngine->SetText(*pData); + else + { + DBG_ERROR("pData == 0"); + } + } + else + { + ULONG nFormat = pPattern->GetNumberFormat( + pDoc->GetFormatTable(), pCondSet ); + String aString; + Color* pColor; + ScCellFormat::GetString( pCell, + nFormat,aString, &pColor, + *pDoc->GetFormatTable(), + bShowNullValues, + bShowFormulas, + ftCheck ); + + pEngine->SetText(aString); + if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) ) + lcl_SetEditColor( *pEngine, *pColor ); + } + + if ( bSyntaxMode ) + SetEditSyntaxColor( *pEngine, pCell ); + else if ( bUseStyleColor && bForceAutoColor ) + lcl_SetEditColor( *pEngine, COL_AUTO ); //! or have a flag at EditEngine + } + else + { + DBG_ERROR("pCell == NULL"); + } + + pEngine->SetUpdateMode( TRUE ); // after SetText, before CalcTextWidth/GetTextHeight + + long nEngineWidth = (long) pEngine->CalcTextWidth(); + long nEngineHeight = pEngine->GetTextHeight(); + + if (nAttrRotate && bBreak) + { + double nAbsCos = fabs( nCos ); + double nAbsSin = fabs( nSin ); + + // #47740# adjust witdh of papersize for height of text + int nSteps = 5; + while (nSteps > 0) + { + // everything is in pixels + long nEnginePixel = pRefDevice->LogicToPixel( + Size(0,nEngineHeight)).Height(); + long nEffHeight = nOutHeight - (long)(nEnginePixel * nAbsCos) + 2; + long nNewWidth = (long)(nEffHeight / nAbsSin) + 2; + BOOL bFits = ( nNewWidth >= aPaperSize.Width() ); + if ( bFits ) + nSteps = 0; + else + { + if ( nNewWidth < 4 ) + { + // can't fit -> fall back to using half height + nEffHeight = nOutHeight / 2; + nNewWidth = (long)(nEffHeight / nAbsSin) + 2; + nSteps = 0; + } + else + --nSteps; + + // set paper width and get new text height + aPaperSize.Width() = nNewWidth; + if (bPixelToLogic) + pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize)); + else + pEngine->SetPaperSize(aPaperSize); // Scale ist immer 1 + //pEngine->QuickFormatDoc( TRUE ); + nEngineWidth = (long) pEngine->CalcTextWidth(); + nEngineHeight = pEngine->GetTextHeight(); + } + } + } + + long nRealWidth = nEngineWidth; + long nRealHeight = nEngineHeight; + + // wenn gedreht, Groesse anpassen + if (nAttrRotate) + { + double nAbsCos = fabs( nCos ); + double nAbsSin = fabs( nSin ); + + if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) + nEngineWidth = (long) ( nRealWidth * nAbsCos + + nRealHeight * nAbsSin ); + else + nEngineWidth = (long) ( nRealHeight / nAbsSin ); + //! begrenzen !!! + + nEngineHeight = (long) ( nRealHeight * nAbsCos + + nRealWidth * nAbsSin ); + } + + if (!nAttrRotate) // hier nur gedrehter Text + bHidden = TRUE; //! vorher abfragen !!! + + //! weglassen, was nicht hereinragt + + if (!bHidden) + { + BOOL bClip = FALSE; + Size aClipSize = Size( nScrX+nScrW-nStartX, nScrY+nScrH-nStartY ); + + // weiterschreiben + + Size aCellSize; + if (bPixelToLogic) + aCellSize = pRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ); + else + aCellSize = Size( nOutWidth, nOutHeight ); // Scale ist 1 + + long nGridWidth = nEngineWidth; + BOOL bNegative = FALSE; + if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) + { + nGridWidth = aCellSize.Width() + + Abs((long) ( aCellSize.Height() * nCos / nSin )); + bNegative = ( pInfo->nRotateDir == SC_ROTDIR_LEFT ); + if ( bLayoutRTL ) + bNegative = !bNegative; + } + + // use GetOutputArea to hide the grid + // (clip region is done manually below) + OutputAreaParam aAreaParam; + + SCCOL nCellX = nX; + SCROW nCellY = nY; + SvxCellHorJustify eOutHorJust = eHorJust; + if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) + eOutHorJust = bNegative ? SVX_HOR_JUSTIFY_RIGHT : SVX_HOR_JUSTIFY_LEFT; + long nNeededWidth = nGridWidth; // in pixel for GetOutputArea + if ( bPixelToLogic ) + nNeededWidth = pRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width(); + + GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth, + *pPattern, sal::static_int_cast<USHORT>(eOutHorJust), + FALSE, FALSE, TRUE, aAreaParam ); + + if ( bShrink ) + { + long nPixelWidth = bPixelToLogic ? + pRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth; + long nNeededPixel = nPixelWidth + nLeftM + nRightM; + + aAreaParam.mbLeftClip = aAreaParam.mbRightClip = TRUE; + + // always do height + ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM, + FALSE, sal::static_int_cast<USHORT>(eOrient), nAttrRotate, bPixelToLogic, + nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); + + if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) + { + // do width only if rotating within the cell (standard mode) + ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM, + TRUE, sal::static_int_cast<USHORT>(eOrient), nAttrRotate, bPixelToLogic, + nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); + } + + // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine + // (but width is only valid for standard mode) + nRealWidth = (long) pEngine->CalcTextWidth(); + nRealHeight = pEngine->GetTextHeight(); + + if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) + nEngineWidth = (long) ( nRealHeight / fabs( nSin ) ); + } + + // BOOL bVClip = ( nEngineHeight > aCellSize.Height() ); + + long nClipStartX = nStartX; + if (nX<nX1) + { + //! Clipping unnoetig, wenn links am Fenster + + bClip = TRUE; // nur Rest ausgeben! + if (nStartX<nScrX) + { + long nDif = nScrX - nStartX; + nClipStartX = nScrX; + aClipSize.Width() -= nDif; + } + } + + long nClipStartY = nStartY; + if (nArrY==0 || bVisChanged) + { + if ( nClipStartY < nRowPosY ) + { + long nDif = nRowPosY - nClipStartY; + bClip = TRUE; + nClipStartY = nRowPosY; + aClipSize.Height() -= nDif; + } + } + + bClip = TRUE; // always clip at the window/page border + + //Rectangle aClipRect; + if (bClip) + { + if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ ) + { + // gedrehten, ausgerichteten Text nur an den + // Seitengrenzen clippen + nClipStartX = nScrX; + aClipSize.Width() = nScrW; + } + + if (bPixelToLogic) + aAreaParam.maClipRect = pRefDevice->PixelToLogic( Rectangle( + Point(nClipStartX,nClipStartY), aClipSize ) ); + else + aAreaParam.maClipRect = Rectangle(Point(nClipStartX, nClipStartY), + aClipSize ); // Scale = 1 + + if (bMetaFile) + { + pDev->Push(); + pDev->IntersectClipRegion( aAreaParam.maClipRect ); + } + else + pDev->SetClipRegion( Region( aAreaParam.maClipRect ) ); + } + + Point aLogicStart; + if (bPixelToLogic) + aLogicStart = pRefDevice->PixelToLogic( Point(nStartX,nStartY) ); + else + aLogicStart = Point(nStartX, nStartY); + if ( eOrient!=SVX_ORIENTATION_STANDARD || !bBreak ) + { + long nAvailWidth = aCellSize.Width(); + if (eType==OUTTYPE_WINDOW && + eOrient!=SVX_ORIENTATION_STACKED && + pInfo && pInfo->bAutoFilter) + { + // filter drop-down width is now independent from row height + if (bPixelToLogic) + nAvailWidth -= pRefDevice->PixelToLogic(Size(0,DROPDOWN_BITMAP_SIZE)).Height(); + else + nAvailWidth -= DROPDOWN_BITMAP_SIZE; + long nComp = nEngineWidth; + if (nAvailWidth<nComp) nAvailWidth=nComp; + } + + // horizontale Ausrichtung + + if (eOrient==SVX_ORIENTATION_STANDARD && !nAttrRotate) + { + if (eHorJust==SVX_HOR_JUSTIFY_RIGHT || + eHorJust==SVX_HOR_JUSTIFY_CENTER) + { + pEngine->SetUpdateMode( FALSE ); + + SvxAdjust eSvxAdjust = + (eHorJust==SVX_HOR_JUSTIFY_RIGHT) ? + SVX_ADJUST_RIGHT : SVX_ADJUST_CENTER; + pEngine->SetDefaultItem( + SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); + + aPaperSize.Width() = nOutWidth; + if (bPixelToLogic) + pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize)); + else + pEngine->SetPaperSize(aPaperSize); + + pEngine->SetUpdateMode( TRUE ); + } + } + else + { + // bei gedrehtem Text ist Standard zentriert + if (eHorJust==SVX_HOR_JUSTIFY_RIGHT) + aLogicStart.X() += nAvailWidth - nEngineWidth; + else if (eHorJust==SVX_HOR_JUSTIFY_CENTER || + eHorJust==SVX_HOR_JUSTIFY_STANDARD) + aLogicStart.X() += (nAvailWidth - nEngineWidth) / 2; + } + } + + if ( bLayoutRTL ) + { + if (bPixelToLogic) + aLogicStart.X() -= pRefDevice->PixelToLogic( + Size( nCellWidth, 0 ) ).Width(); + else + aLogicStart.X() -= nCellWidth; + } + + if ( eOrient==SVX_ORIENTATION_STANDARD || + eOrient==SVX_ORIENTATION_STACKED || !bBreak ) + { + if (eVerJust==SVX_VER_JUSTIFY_BOTTOM || + eVerJust==SVX_VER_JUSTIFY_STANDARD) + { + if (bPixelToLogic) + aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0, + pRefDevice->LogicToPixel(aCellSize).Height() - + pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() + )).Height(); + else + aLogicStart.Y() += aCellSize.Height() - nEngineHeight; + } + + else if (eVerJust==SVX_VER_JUSTIFY_CENTER) + { + if (bPixelToLogic) + aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0,( + pRefDevice->LogicToPixel(aCellSize).Height() - + pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()) + / 2)).Height(); + else + aLogicStart.Y() += (aCellSize.Height() - nEngineHeight) / 2; + } + } + + // TOPBOTTON and BOTTOMTOP are handled in DrawStrings/DrawEdit + DBG_ASSERT( eOrient == SVX_ORIENTATION_STANDARD && nAttrRotate, + "DrawRotated: no rotation" ); + + long nOriVal = 0; + if ( nAttrRotate ) + { + // Attribut ist 1/100, Font 1/10 Grad + nOriVal = nAttrRotate / 10; + + double nAddX = 0.0; + double nAddY = 0.0; + if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD ) + { + //! begrenzen !!! + double nH = nRealHeight * nCos; + nAddX += nH * ( nCos / fabs(nSin) ); + } + if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD ) + nAddX -= nRealWidth * nCos; + if ( nSin < 0.0 ) + nAddX -= nRealHeight * nSin; + if ( nSin > 0.0 ) + nAddY += nRealWidth * nSin; + if ( nCos < 0.0 ) + nAddY -= nRealHeight * nCos; + + if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) + { + //! begrenzen !!! + double nSkew = nTotalHeight * nCos / fabs(nSin); + if ( eRotMode == SVX_ROTATE_MODE_CENTER ) + nAddX -= nSkew * 0.5; + if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) || + ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) ) + nAddX -= nSkew; + + long nUp = 0; + if ( eVerJust == SVX_VER_JUSTIFY_CENTER ) + nUp = ( aCellSize.Height() - nEngineHeight ) / 2; + else if ( eVerJust == SVX_VER_JUSTIFY_TOP ) + { + if ( nSin > 0.0 ) + nUp = aCellSize.Height() - nEngineHeight; + } + else // BOTTOM / STANDARD + { + if ( nSin < 0.0 ) + nUp = aCellSize.Height() - nEngineHeight; + } + if ( nUp ) + nAddX += ( nUp * nCos / fabs(nSin) ); + } + + aLogicStart.X() += (long) nAddX; + aLogicStart.Y() += (long) nAddY; + } + + // bSimClip is not used here (because nOriVal is set) + + if ( pEngine->IsRightToLeft( 0 ) ) + { + // For right-to-left, EditEngine always calculates its lines + // beginning from the right edge, but EditLine::nStartPosX is + // of USHORT type, so the PaperSize must be limited to USHRT_MAX. + Size aLogicPaper = pEngine->GetPaperSize(); + if ( aLogicPaper.Width() > USHRT_MAX ) + { + aLogicPaper.Width() = USHRT_MAX; + pEngine->SetPaperSize(aLogicPaper); + } + } + + pEngine->Draw( pDev, aLogicStart, (short)nOriVal ); + + if (bClip) + { + if (bMetaFile) + pDev->Pop(); + else + pDev->SetClipRegion(); + } + } + } + } + } + nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign; + } + } + nRowPosY += pRowInfo[nArrY].nHeight; + } + + delete pEngine; +} + + + |