blob: b608248b7ebfdc5a98a36c947bbcc8dbe782e20f [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
// INCLUDE ---------------------------------------------------------------
#include "scitems.hxx"
#include <editeng/eeitem.hxx>
#include <svx/algitem.hxx>
#include <editeng/editobj.hxx>
#include <editeng/editstat.hxx>
#include <editeng/emphitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/forbiddencharacterstable.hxx>
#include <svx/rotmodit.hxx>
#include <editeng/scripttypeitem.hxx>
#include <editeng/unolingu.hxx>
#include <svl/zforlist.hxx>
#include <svl/broadcast.hxx>
#include <svl/listeneriter.hxx>
#include <vcl/outdev.hxx>
#include "column.hxx"
#include "cell.hxx"
#include "document.hxx"
#include "docpool.hxx"
#include "attarray.hxx"
#include "patattr.hxx"
#include "cellform.hxx"
#include "collect.hxx"
#include "stlsheet.hxx"
#include "rechead.hxx"
#include "brdcst.hxx"
#include "editutil.hxx"
#include "subtotal.hxx"
#include "markdata.hxx"
#include "compiler.hxx" // ScTokenArray GetCodeLen
#include "dbcolect.hxx"
#include "fillinfo.hxx"
#include "segmenttree.hxx"
#include <math.h>
// -----------------------------------------------------------------------
// factor from font size to optimal cell height (text width)
#define SC_ROT_BREAK_FACTOR 6
// -----------------------------------------------------------------------
inline sal_Bool IsAmbiguousScript( sal_uInt8 nScript )
{
//! move to a header file
return ( nScript != SCRIPTTYPE_LATIN &&
nScript != SCRIPTTYPE_ASIAN &&
nScript != SCRIPTTYPE_COMPLEX );
}
// -----------------------------------------------------------------------------------------
//
// Datei-Operationen
//
// -----------------------------------------------------------------------------------------
//UNUSED2008-05 SCROW ScColumn::NoteCount( SCROW nMaxRow ) const
//UNUSED2008-05 {
//UNUSED2008-05 SCROW nNoteCount = 0;
//UNUSED2008-05 SCSIZE i;
//UNUSED2008-05
//UNUSED2008-05 for (i=0; i<nCount; i++)
//UNUSED2008-05 if ( pItems[i].pCell->GetNotePtr() && pItems[i].nRow<=nMaxRow )
//UNUSED2008-05 ++nNoteCount;
//UNUSED2008-05
//UNUSED2008-05 return nNoteCount;
//UNUSED2008-05 }
// -----------------------------------------------------------------------------------------
//UNUSED2008-05 void ScColumn::CorrectSymbolCells( CharSet eStreamCharSet )
//UNUSED2008-05 {
//UNUSED2008-05 // #99139# find and correct string cells that are formatted with a symbol font,
//UNUSED2008-05 // but are not in the LoadedSymbolStringCellsList
//UNUSED2008-05 // (because CELLTYPE_SYMBOLS wasn't written in the file)
//UNUSED2008-05
//UNUSED2008-05 ScFontToSubsFontConverter_AutoPtr xFontConverter;
//UNUSED2008-05 const sal_uLong nFontConverterFlags = FONTTOSUBSFONT_EXPORT | FONTTOSUBSFONT_ONLYOLDSOSYMBOLFONTS;
//UNUSED2008-05
//UNUSED2008-05 sal_Bool bListInitialized = sal_False;
//UNUSED2008-05 ScSymbolStringCellEntry* pCurrentEntry = NULL;
//UNUSED2008-05
//UNUSED2008-05 ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
//UNUSED2008-05 SCROW nStt, nEnd;
//UNUSED2008-05 const ScPatternAttr* pAttr = aAttrIter.Next( nStt, nEnd );
//UNUSED2008-05 while ( pAttr )
//UNUSED2008-05 {
//UNUSED2008-05 if ( (xFontConverter = pAttr->GetSubsFontConverter( nFontConverterFlags )) ||
//UNUSED2008-05 pAttr->IsSymbolFont() )
//UNUSED2008-05 {
//UNUSED2008-05 ScColumnIterator aCellIter( this, nStt, nEnd );
//UNUSED2008-05 SCROW nRow;
//UNUSED2008-05 ScBaseCell* pCell;
//UNUSED2008-05 while ( aCellIter.Next( nRow, pCell ) )
//UNUSED2008-05 {
//UNUSED2008-05 if ( pCell->GetCellType() == CELLTYPE_STRING )
//UNUSED2008-05 {
//UNUSED2008-05 List& rList = pDocument->GetLoadedSymbolStringCellsList();
//UNUSED2008-05 if (!bListInitialized)
//UNUSED2008-05 {
//UNUSED2008-05 pCurrentEntry = (ScSymbolStringCellEntry*)rList.First();
//UNUSED2008-05 bListInitialized = sal_True;
//UNUSED2008-05 }
//UNUSED2008-05
//UNUSED2008-05 while ( pCurrentEntry && pCurrentEntry->nRow < nRow )
//UNUSED2008-05 pCurrentEntry = (ScSymbolStringCellEntry*)rList.Next();
//UNUSED2008-05
//UNUSED2008-05 if ( pCurrentEntry && pCurrentEntry->nRow == nRow )
//UNUSED2008-05 {
//UNUSED2008-05 // found
//UNUSED2008-05 }
//UNUSED2008-05 else
//UNUSED2008-05 {
//UNUSED2008-05 // not in list -> convert and put into list
//UNUSED2008-05
//UNUSED2008-05 ScStringCell* pStrCell = (ScStringCell*)pCell;
//UNUSED2008-05 String aOldStr;
//UNUSED2008-05 pStrCell->GetString( aOldStr );
//UNUSED2008-05
//UNUSED2008-05 // convert back to stream character set (get original data)
//UNUSED2008-05 ByteString aByteStr( aOldStr, eStreamCharSet );
//UNUSED2008-05
//UNUSED2008-05 // convert using symbol encoding, as for CELLTYPE_SYMBOLS cells
//UNUSED2008-05 String aNewStr( aByteStr, RTL_TEXTENCODING_SYMBOL );
//UNUSED2008-05 pStrCell->SetString( aNewStr );
//UNUSED2008-05
//UNUSED2008-05 ScSymbolStringCellEntry * pEntry = new ScSymbolStringCellEntry;
//UNUSED2008-05 pEntry->pCell = pStrCell;
//UNUSED2008-05 pEntry->nRow = nRow;
//UNUSED2008-05
//UNUSED2008-05 if ( pCurrentEntry )
//UNUSED2008-05 rList.Insert( pEntry ); // before current entry - pCurrentEntry stays valid
//UNUSED2008-05 else
//UNUSED2008-05 rList.Insert( pEntry, LIST_APPEND ); // append if already behind last entry
//UNUSED2008-05 }
//UNUSED2008-05 }
//UNUSED2008-05 }
//UNUSED2008-05 }
//UNUSED2008-05
//UNUSED2008-05 pAttr = aAttrIter.Next( nStt, nEnd );
//UNUSED2008-05 }
//UNUSED2008-05 }
// -----------------------------------------------------------------------------------------
// GetNeededSize: optimale Hoehe / Breite in Pixeln
long ScColumn::GetNeededSize( SCROW nRow, OutputDevice* pDev,
double nPPTX, double nPPTY,
const Fraction& rZoomX, const Fraction& rZoomY,
sal_Bool bWidth, const ScNeededSizeOptions& rOptions )
{
long nValue=0;
SCSIZE nIndex;
double nPPT = bWidth ? nPPTX : nPPTY;
if (Search(nRow,nIndex))
{
ScBaseCell* pCell = pItems[nIndex].pCell;
const ScPatternAttr* pPattern = rOptions.pPattern;
if (!pPattern)
pPattern = pAttrArray->GetPattern( nRow );
// zusammengefasst?
// Merge nicht in bedingter Formatierung
const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE);
const ScMergeFlagAttr* pFlag = (const ScMergeFlagAttr*)&pPattern->GetItem(ATTR_MERGE_FLAG);
if ( bWidth )
{
if ( pFlag->IsHorOverlapped() )
return 0;
if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 )
return 0;
}
else
{
if ( pFlag->IsVerOverlapped() )
return 0;
if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 )
return 0;
}
// bedingte Formatierung
const SfxItemSet* pCondSet = NULL;
if ( ((const SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() )
pCondSet = pDocument->GetCondResult( nCol, nRow, nTab );
// Zeilenumbruch?
const SfxPoolItem* pCondItem;
SvxCellHorJustify eHorJust;
if (pCondSet &&
pCondSet->GetItemState(ATTR_HOR_JUSTIFY, sal_True, &pCondItem) == SFX_ITEM_SET)
eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem*)pCondItem)->GetValue();
else
eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&)
pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue();
bool bBreak;
if ( eHorJust == SVX_HOR_JUSTIFY_BLOCK )
bBreak = true;
else if ( pCondSet &&
pCondSet->GetItemState(ATTR_LINEBREAK, sal_True, &pCondItem) == SFX_ITEM_SET)
bBreak = ((const SfxBoolItem*)pCondItem)->GetValue();
else
bBreak = ((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK)).GetValue();
SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
sal_uLong nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
// #i111387# #o11817313# disable automatic line breaks only for "General" number format
if ( bBreak && pCell->HasValueData() && ( nFormat % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 )
{
// also take formula result type into account for number format
if ( pCell->GetCellType() != CELLTYPE_FORMULA ||
( static_cast<ScFormulaCell*>(pCell)->GetStandardFormat(*pFormatter, nFormat) % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 )
bBreak = false;
}
// get other attributes from pattern and conditional formatting
SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
sal_Bool bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED &&
((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() );
if ( bAsianVertical )
bBreak = false;
if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset)
return 0;
long nRotate = 0;
SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
if ( eOrient == SVX_ORIENTATION_STANDARD )
{
if (pCondSet &&
pCondSet->GetItemState(ATTR_ROTATE_VALUE, sal_True, &pCondItem) == SFX_ITEM_SET)
nRotate = ((const SfxInt32Item*)pCondItem)->GetValue();
else
nRotate = ((const SfxInt32Item&)pPattern->GetItem(ATTR_ROTATE_VALUE)).GetValue();
if ( nRotate )
{
if (pCondSet &&
pCondSet->GetItemState(ATTR_ROTATE_MODE, sal_True, &pCondItem) == SFX_ITEM_SET)
eRotMode = (SvxRotateMode)((const SvxRotateModeItem*)pCondItem)->GetValue();
else
eRotMode = (SvxRotateMode)((const SvxRotateModeItem&)
pPattern->GetItem(ATTR_ROTATE_MODE)).GetValue();
if ( nRotate == 18000 )
eRotMode = SVX_ROTATE_MODE_STANDARD; // keinen Ueberlauf
}
}
if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT )
{
// ignore orientation/rotation if "repeat" is active
eOrient = SVX_ORIENTATION_STANDARD;
nRotate = 0;
bAsianVertical = sal_False;
}
const SvxMarginItem* pMargin;
if (pCondSet &&
pCondSet->GetItemState(ATTR_MARGIN, sal_True, &pCondItem) == SFX_ITEM_SET)
pMargin = (const SvxMarginItem*) pCondItem;
else
pMargin = (const SvxMarginItem*) &pPattern->GetItem(ATTR_MARGIN);
sal_uInt16 nIndent = 0;
if ( eHorJust == SVX_HOR_JUSTIFY_LEFT )
{
if (pCondSet &&
pCondSet->GetItemState(ATTR_INDENT, sal_True, &pCondItem) == SFX_ITEM_SET)
nIndent = ((const SfxUInt16Item*)pCondItem)->GetValue();
else
nIndent = ((const SfxUInt16Item&)pPattern->GetItem(ATTR_INDENT)).GetValue();
}
sal_uInt8 nScript = pDocument->GetScriptType( nCol, nRow, nTab, pCell );
if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType();
// also call SetFont for edit cells, because bGetFont may be set only once
// bGetFont is set also if script type changes
if (rOptions.bGetFont)
{
Fraction aFontZoom = ( eOrient == SVX_ORIENTATION_STANDARD ) ? rZoomX : rZoomY;
Font aFont;
// font color doesn't matter here
pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aFontZoom, pCondSet, nScript );
pDev->SetFont(aFont);
}
sal_Bool bAddMargin = sal_True;
CellType eCellType = pCell->GetCellType();
sal_Bool bEditEngine = ( eCellType == CELLTYPE_EDIT ||
eOrient == SVX_ORIENTATION_STACKED ||
IsAmbiguousScript( nScript ) ||
((eCellType == CELLTYPE_FORMULA) && ((ScFormulaCell*)pCell)->IsMultilineResult()) );
if (!bEditEngine) // direkte Ausgabe
{
String aValStr;
Color* pColor;
ScCellFormat::GetString( pCell, nFormat, aValStr, &pColor,
*pFormatter,
sal_True, rOptions.bFormula, ftCheck );
if (aValStr.Len())
{
// SetFont ist nach oben verschoben
Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() );
if ( eOrient != SVX_ORIENTATION_STANDARD )
{
long nTemp = aSize.Width();
aSize.Width() = aSize.Height();
aSize.Height() = nTemp;
}
else if ( nRotate )
{
//! unterschiedliche Skalierung X/Y beruecksichtigen
double nRealOrient = nRotate * F_PI18000; // nRotate sind 1/100 Grad
double nCosAbs = fabs( cos( nRealOrient ) );
double nSinAbs = fabs( sin( nRealOrient ) );
long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
long nWidth;
if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
else if ( rOptions.bTotalSize )
{
nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT );
bAddMargin = sal_False;
// nur nach rechts:
//! unterscheiden nach Ausrichtung oben/unten (nur Text/ganze Hoehe)
if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT )
nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) *
nPPT * nCosAbs / nSinAbs );
}
else
nWidth = (long)( aSize.Height() / nSinAbs ); //! begrenzen?
if ( bBreak && !rOptions.bTotalSize )
{
// #47744# limit size for line break
long nCmp = pDev->GetFont().GetSize().Height() * SC_ROT_BREAK_FACTOR;
if ( nHeight > nCmp )
nHeight = nCmp;
}
aSize = Size( nWidth, nHeight );
}
nValue = bWidth ? aSize.Width() : aSize.Height();
if ( bAddMargin )
{
if (bWidth)
{
nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) +
(long) ( pMargin->GetRightMargin() * nPPT );
if ( nIndent )
nValue += (long) ( nIndent * nPPT );
}
else
nValue += (long) ( pMargin->GetTopMargin() * nPPT ) +
(long) ( pMargin->GetBottomMargin() * nPPT );
}
// Zeilenumbruch ausgefuehrt ?
if ( bBreak && !bWidth )
{
// Test mit EditEngine zur Sicherheit schon bei 90%
// (wegen Rundungsfehlern und weil EditEngine teilweise anders formatiert)
long nDocPixel = (long) ( ( pDocument->GetColWidth( nCol,nTab ) -
pMargin->GetLeftMargin() - pMargin->GetRightMargin() -
nIndent )
* nPPT );
nDocPixel = (nDocPixel * 9) / 10; // zur Sicherheit
if ( aSize.Width() > nDocPixel )
bEditEngine = sal_True;
}
}
}
if (bEditEngine)
{
// der Font wird bei !bEditEngine nicht jedesmal neu gesetzt
Font aOldFont = pDev->GetFont();
MapMode aHMMMode( MAP_100TH_MM, Point(), rZoomX, rZoomY );
// am Dokument speichern ?
ScFieldEditEngine* pEngine = pDocument->CreateFieldEditEngine();
pEngine->SetUpdateMode( sal_False );
sal_Bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER );
sal_uInt32 nCtrl = pEngine->GetControlWord();
if ( bTextWysiwyg )
nCtrl |= EE_CNTRL_FORMAT100;
else
nCtrl &= ~EE_CNTRL_FORMAT100;
pEngine->SetControlWord( nCtrl );
MapMode aOld = pDev->GetMapMode();
pDev->SetMapMode( aHMMMode );
pEngine->SetRefDevice( pDev );
pDocument->ApplyAsianEditSettings( *pEngine );
SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() );
pPattern->FillEditItemSet( pSet, pCondSet );
// no longer needed, are setted with the text (is faster)
// pEngine->SetDefaults( pSet );
if ( ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) {
com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
pEngine->SetHyphenator( xXHyphenator );
}
Size aPaper = Size( 1000000, 1000000 );
if ( eOrient==SVX_ORIENTATION_STACKED && !bAsianVertical )
aPaper.Width() = 1;
else if (bBreak)
{
double fWidthFactor = nPPTX;
if ( bTextWysiwyg )
{
// #95593# if text is formatted for printer, don't use PixelToLogic,
// to ensure the exact same paper width (and same line breaks) as in
// ScEditUtil::GetEditArea, used for output.
fWidthFactor = HMM_PER_TWIPS;
}
// use original width for hidden columns:
long nDocWidth = (long) ( pDocument->GetOriginalWidth(nCol,nTab) * fWidthFactor );
SCCOL nColMerge = pMerge->GetColMerge();
if (nColMerge > 1)
for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++)
nDocWidth += (long) ( pDocument->GetColWidth(nCol+nColAdd,nTab) * fWidthFactor );
nDocWidth -= (long) ( pMargin->GetLeftMargin() * fWidthFactor )
+ (long) ( pMargin->GetRightMargin() * fWidthFactor )
+ 1; // Ausgabebereich ist Breite-1 Pixel (wegen Gitterlinien)
if ( nIndent )
nDocWidth -= (long) ( nIndent * fWidthFactor );
// space for AutoFilter button: 20 * nZoom/100
if ( pFlag->HasAutoFilter() && !bTextWysiwyg )
nDocWidth -= (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator();
aPaper.Width() = nDocWidth;
if ( !bTextWysiwyg )
aPaper = pDev->PixelToLogic( aPaper, aHMMMode );
}
pEngine->SetPaperSize(aPaper);
if ( pCell->GetCellType() == CELLTYPE_EDIT )
{
const EditTextObject* pData;
((ScEditCell*)pCell)->GetData(pData);
pEngine->SetTextNewDefaults(*pData, pSet);
}
else
{
Color* pColor;
String aString;
ScCellFormat::GetString( pCell, nFormat, aString, &pColor,
*pFormatter,
sal_True, rOptions.bFormula, ftCheck );
if (aString.Len())
pEngine->SetTextNewDefaults(aString, pSet);
else
pEngine->SetDefaults(pSet);
}
sal_Bool bEngineVertical = pEngine->IsVertical();
pEngine->SetVertical( bAsianVertical );
pEngine->SetUpdateMode( sal_True );
sal_Bool bEdWidth = bWidth;
if ( eOrient != SVX_ORIENTATION_STANDARD && eOrient != SVX_ORIENTATION_STACKED )
bEdWidth = !bEdWidth;
if ( nRotate )
{
//! unterschiedliche Skalierung X/Y beruecksichtigen
Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() );
double nRealOrient = nRotate * F_PI18000; // nRotate sind 1/100 Grad
double nCosAbs = fabs( cos( nRealOrient ) );
double nSinAbs = fabs( sin( nRealOrient ) );
long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
long nWidth;
if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
else if ( rOptions.bTotalSize )
{
nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT );
bAddMargin = sal_False;
if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT )
nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) *
nPPT * nCosAbs / nSinAbs );
}
else
nWidth = (long)( aSize.Height() / nSinAbs ); //! begrenzen?
aSize = Size( nWidth, nHeight );
Size aPixSize = pDev->LogicToPixel( aSize, aHMMMode );
if ( bEdWidth )
nValue = aPixSize.Width();
else
{
nValue = aPixSize.Height();
if ( bBreak && !rOptions.bTotalSize )
{
// #47744# limit size for line break
long nCmp = aOldFont.GetSize().Height() * SC_ROT_BREAK_FACTOR;
if ( nValue > nCmp )
nValue = nCmp;
}
}
}
else if ( bEdWidth )
{
if (bBreak)
nValue = 0;
else
nValue = pDev->LogicToPixel(Size( pEngine->CalcTextWidth(), 0 ),
aHMMMode).Width();
}
else // Hoehe
{
nValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ),
aHMMMode).Height();
// With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set
if ( !bTextWysiwyg && ( rZoomY.GetNumerator() != 1 || rZoomY.GetDenominator() != 1 ) &&
( pEngine->GetParagraphCount() > 1 || ( bBreak && pEngine->GetLineCount(0) > 1 ) ) )
{
pEngine->SetControlWord( nCtrl | EE_CNTRL_FORMAT100 );
pEngine->QuickFormatDoc( sal_True );
long nSecondValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), aHMMMode).Height();
if ( nSecondValue > nValue )
nValue = nSecondValue;
}
}
if ( nValue && bAddMargin )
{
if (bWidth)
{
nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) +
(long) ( pMargin->GetRightMargin() * nPPT );
if (nIndent)
nValue += (long) ( nIndent * nPPT );
}
else
{
nValue += (long) ( pMargin->GetTopMargin() * nPPT ) +
(long) ( pMargin->GetBottomMargin() * nPPT );
if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER )
{
// add 1pt extra (default margin value) for line breaks with SetVertical
nValue += (long) ( 20 * nPPT );
}
}
}
// EditEngine is cached and re-used, so the old vertical flag must be restored
pEngine->SetVertical( bEngineVertical );
pDocument->DisposeFieldEditEngine(pEngine);
pDev->SetMapMode( aOld );
pDev->SetFont( aOldFont );
}
if (bWidth)
{
// Platz fuer Autofilter-Button
// 20 * nZoom/100
// bedingte Formatierung hier nicht interessant
sal_Int16 nFlags = ((const ScMergeFlagAttr&)pPattern->GetItem(ATTR_MERGE_FLAG)).GetValue();
if (nFlags & SC_MF_AUTO)
nValue += (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator();
}
}
return nValue;
}
long ScColumn::GetSimpleTextNeededSize( SCSIZE nIndex, OutputDevice* pDev,
sal_Bool bWidth )
{
long nValue=0;
if ( nIndex < nCount )
{
SCROW nRow = pItems[nIndex].nRow;
const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
ScBaseCell* pCell = pItems[nIndex].pCell;
String aValStr;
Color* pColor;
SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
sal_uLong nFormat = pPattern->GetNumberFormat( pFormatter );
ScCellFormat::GetString( pCell, nFormat, aValStr, &pColor,
*pFormatter, sal_True, sal_False, ftCheck );
if ( aValStr.Len() )
{
if ( bWidth )
nValue = pDev->GetTextWidth( aValStr );
else
nValue = pDev->GetTextHeight();
}
}
return nValue;
}
sal_uInt16 ScColumn::GetOptimalColWidth( OutputDevice* pDev, double nPPTX, double nPPTY,
const Fraction& rZoomX, const Fraction& rZoomY,
sal_Bool bFormula, sal_uInt16 nOldWidth,
const ScMarkData* pMarkData,
sal_Bool bSimpleTextImport )
{
if (nCount == 0)
return nOldWidth;
sal_uInt16 nWidth = (sal_uInt16) (nOldWidth * nPPTX);
sal_Bool bFound = sal_False;
SCSIZE nIndex;
ScMarkedDataIter aDataIter(this, pMarkData, sal_True);
if ( bSimpleTextImport )
{ // alles eins bis auf NumberFormate
const ScPatternAttr* pPattern = GetPattern( 0 );
Font aFont;
// font color doesn't matter here
pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &rZoomX, NULL );
pDev->SetFont( aFont );
const SvxMarginItem* pMargin = (const SvxMarginItem*) &pPattern->GetItem(ATTR_MARGIN);
long nMargin = (long) ( pMargin->GetLeftMargin() * nPPTX ) +
(long) ( pMargin->GetRightMargin() * nPPTX );
while (aDataIter.Next( nIndex ))
{
sal_uInt16 nThis = (sal_uInt16) (GetSimpleTextNeededSize( nIndex, pDev,
sal_True ) + nMargin);
if (nThis)
{
if (nThis>nWidth || !bFound)
{
nWidth = nThis;
bFound = sal_True;
}
}
}
}
else
{
ScNeededSizeOptions aOptions;
aOptions.bFormula = bFormula;
const ScPatternAttr* pOldPattern = NULL;
sal_uInt8 nOldScript = 0;
while (aDataIter.Next( nIndex ))
{
SCROW nRow = pItems[nIndex].nRow;
sal_uInt8 nScript = pDocument->GetScriptType( nCol, nRow, nTab, pItems[nIndex].pCell );
if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType();
const ScPatternAttr* pPattern = GetPattern( nRow );
aOptions.pPattern = pPattern;
aOptions.bGetFont = (pPattern != pOldPattern || nScript != nOldScript);
sal_uInt16 nThis = (sal_uInt16) GetNeededSize( nRow, pDev, nPPTX, nPPTY,
rZoomX, rZoomY, sal_True, aOptions );
pOldPattern = pPattern;
if (nThis)
{
if (nThis>nWidth || !bFound)
{
nWidth = nThis;
bFound = sal_True;
}
}
}
}
if (bFound)
{
nWidth += 2;
sal_uInt16 nTwips = (sal_uInt16) (nWidth / nPPTX);
return nTwips;
}
else
return nOldWidth;
}
sal_uInt16 lcl_GetAttribHeight( const ScPatternAttr& rPattern, sal_uInt16 nFontHeightId )
{
sal_uInt16 nHeight = (sal_uInt16) ((const SvxFontHeightItem&) rPattern.GetItem(nFontHeightId)).GetHeight();
const SvxMarginItem* pMargin = (const SvxMarginItem*) &rPattern.GetItem(ATTR_MARGIN);
nHeight += nHeight / 5;
// gibt bei 10pt 240
if ( ((const SvxEmphasisMarkItem&)rPattern.
GetItem(ATTR_FONT_EMPHASISMARK)).GetEmphasisMark() != EMPHASISMARK_NONE )
{
// add height for emphasis marks
//! font metrics should be used instead
nHeight += nHeight / 4;
}
if ( nHeight + 240 > ScGlobal::nDefFontHeight )
{
nHeight = sal::static_int_cast<sal_uInt16>( nHeight + ScGlobal::nDefFontHeight );
nHeight -= 240;
}
// Standard-Hoehe: TextHeight + Raender - 23
// -> 257 unter Windows
if (nHeight > STD_ROWHEIGHT_DIFF)
nHeight -= STD_ROWHEIGHT_DIFF;
nHeight += pMargin->GetTopMargin() + pMargin->GetBottomMargin();
return nHeight;
}
// pHeight in Twips
// nMinHeight, nMinStart zur Optimierung: ab nRow >= nMinStart ist mindestens nMinHeight
// (wird nur bei bStdAllowed ausgewertet)
void ScColumn::GetOptimalHeight( SCROW nStartRow, SCROW nEndRow, sal_uInt16* pHeight,
OutputDevice* pDev,
double nPPTX, double nPPTY,
const Fraction& rZoomX, const Fraction& rZoomY,
sal_Bool bShrink, sal_uInt16 nMinHeight, SCROW nMinStart )
{
ScAttrIterator aIter( pAttrArray, nStartRow, nEndRow );
SCROW nStart = -1;
SCROW nEnd = -1;
SCROW nEditPos = 0;
SCROW nNextEnd = 0;
// bei bedingter Formatierung werden immer die einzelnen Zellen angesehen
const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd);
while ( pPattern )
{
const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE);
const ScMergeFlagAttr* pFlag = (const ScMergeFlagAttr*)&pPattern->GetItem(ATTR_MERGE_FLAG);
if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() )
{
// nix - vertikal bei der zusammengefassten und den ueberdeckten,
// horizontal nur bei den ueberdeckten (unsichtbaren) -
// eine nur horizontal zusammengefasste wird aber beruecksichtigt
}
else
{
SCROW nRow = 0;
sal_Bool bStdAllowed = (pPattern->GetCellOrientation() == SVX_ORIENTATION_STANDARD);
sal_Bool bStdOnly = sal_False;
if (bStdAllowed)
{
sal_Bool bBreak = ((SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK)).GetValue() ||
((SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern->
GetItem( ATTR_HOR_JUSTIFY )).GetValue() ==
SVX_HOR_JUSTIFY_BLOCK);
bStdOnly = !bBreak;
// bedingte Formatierung: Zellen durchgehen
if ( bStdOnly && ((const SfxUInt32Item&)pPattern->
GetItem(ATTR_CONDITIONAL)).GetValue() )
bStdOnly = sal_False;
// gedrehter Text: Zellen durchgehen
if ( bStdOnly && ((const SfxInt32Item&)pPattern->
GetItem(ATTR_ROTATE_VALUE)).GetValue() )
bStdOnly = sal_False;
}
if (bStdOnly)
if (HasEditCells(nStart,nEnd,nEditPos)) // includes mixed script types
{
if (nEditPos == nStart)
{
bStdOnly = sal_False;
if (nEnd > nEditPos)
nNextEnd = nEnd;
nEnd = nEditPos; // einzeln ausrechnen
bStdAllowed = sal_False; // wird auf jeden Fall per Zelle berechnet
}
else
{
nNextEnd = nEnd;
nEnd = nEditPos - 1; // Standard - Teil
}
}
if (bStdAllowed)
{
sal_uInt16 nLatHeight = 0;
sal_uInt16 nCjkHeight = 0;
sal_uInt16 nCtlHeight = 0;
sal_uInt16 nDefHeight;
sal_uInt8 nDefScript = ScGlobal::GetDefaultScriptType();
if ( nDefScript == SCRIPTTYPE_ASIAN )
nDefHeight = nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT );
else if ( nDefScript == SCRIPTTYPE_COMPLEX )
nDefHeight = nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT );
else
nDefHeight = nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
// if everything below is already larger, the loop doesn't have to
// be run again
SCROW nStdEnd = nEnd;
if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
for (nRow=nStart; nRow<=nStdEnd; nRow++)
if (nDefHeight > pHeight[nRow-nStartRow])
pHeight[nRow-nStartRow] = nDefHeight;
if ( bStdOnly )
{
// if cells are not handled individually below,
// check for cells with different script type
SCSIZE nIndex;
Search(nStart,nIndex);
while ( nIndex < nCount && (nRow=pItems[nIndex].nRow) <= nEnd )
{
sal_uInt8 nScript = pDocument->GetScriptType( nCol, nRow, nTab, pItems[nIndex].pCell );
if ( nScript != nDefScript )
{
if ( nScript == SCRIPTTYPE_ASIAN )
{
if ( nCjkHeight == 0 )
nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT );
if (nCjkHeight > pHeight[nRow-nStartRow])
pHeight[nRow-nStartRow] = nCjkHeight;
}
else if ( nScript == SCRIPTTYPE_COMPLEX )
{
if ( nCtlHeight == 0 )
nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT );
if (nCtlHeight > pHeight[nRow-nStartRow])
pHeight[nRow-nStartRow] = nCtlHeight;
}
else
{
if ( nLatHeight == 0 )
nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
if (nLatHeight > pHeight[nRow-nStartRow])
pHeight[nRow-nStartRow] = nLatHeight;
}
}
++nIndex;
}
}
}
if (!bStdOnly) // belegte Zellen suchen
{
ScNeededSizeOptions aOptions;
SCSIZE nIndex;
Search(nStart,nIndex);
while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEnd) : sal_False )
{
// Zellhoehe nur berechnen, wenn sie spaeter auch gebraucht wird (#37928#)
if ( bShrink || !(pDocument->GetRowFlags(nRow, nTab) & CR_MANUALSIZE) )
{
aOptions.pPattern = pPattern;
sal_uInt16 nHeight = (sal_uInt16)
( GetNeededSize( nRow, pDev, nPPTX, nPPTY,
rZoomX, rZoomY, sal_False, aOptions ) / nPPTY );
if (nHeight > pHeight[nRow-nStartRow])
pHeight[nRow-nStartRow] = nHeight;
}
++nIndex;
}
}
}
if (nNextEnd > 0)
{
nStart = nEnd + 1;
nEnd = nNextEnd;
nNextEnd = 0;
}
else
pPattern = aIter.Next(nStart,nEnd);
}
}
sal_Bool ScColumn::GetNextSpellingCell(SCROW& nRow, sal_Bool bInSel, const ScMarkData& rData) const
{
sal_Bool bStop = sal_False;
CellType eCellType;
SCSIZE nIndex;
if (!bInSel && Search(nRow, nIndex))
{
eCellType = GetCellType(nRow);
if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) &&
!(HasAttrib( nRow, nRow, HASATTR_PROTECTED) &&
pDocument->IsTabProtected(nTab)) )
return sal_True;
}
while (!bStop)
{
if (bInSel)
{
nRow = rData.GetNextMarked(nCol, nRow, sal_False);
if (!ValidRow(nRow))
{
nRow = MAXROW+1;
bStop = sal_True;
}
else
{
eCellType = GetCellType(nRow);
if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) &&
!(HasAttrib( nRow, nRow, HASATTR_PROTECTED) &&
pDocument->IsTabProtected(nTab)) )
return sal_True;
else
nRow++;
}
}
else if (GetNextDataPos(nRow))
{
eCellType = GetCellType(nRow);
if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) &&
!(HasAttrib( nRow, nRow, HASATTR_PROTECTED) &&
pDocument->IsTabProtected(nTab)) )
return sal_True;
else
nRow++;
}
else
{
nRow = MAXROW+1;
bStop = sal_True;
}
}
return sal_False;
}
// =========================================================================================
void ScColumn::RemoveAutoSpellObj()
{
ScTabEditEngine* pEngine = NULL;
for (SCSIZE i=0; i<nCount; i++)
if ( pItems[i].pCell->GetCellType() == CELLTYPE_EDIT )
{
ScEditCell* pOldCell = (ScEditCell*) pItems[i].pCell;
const EditTextObject* pData = pOldCell->GetData();
// keine Abfrage auf HasOnlineSpellErrors, damit es auch
// nach dem Laden funktioniert
// Fuer den Test auf harte Formatierung (ScEditAttrTester) sind die Defaults
// in der EditEngine unwichtig. Wenn der Tester spaeter einmal gleiche
// Attribute in Default und harter Formatierung erkennen und weglassen sollte,
// muessten an der EditEngine zu jeder Zelle die richtigen Defaults gesetzt
// werden!
// auf Attribute testen
if ( !pEngine )
pEngine = new ScTabEditEngine(pDocument);
pEngine->SetText( *pData );
ScEditAttrTester aTester( pEngine );
if ( aTester.NeedsObject() ) // nur Spell-Errors entfernen
{
EditTextObject* pNewData = pEngine->CreateTextObject(); // ohne BIGOBJ
pOldCell->SetData( pNewData, pEngine->GetEditTextObjectPool() );
delete pNewData;
}
else // String erzeugen
{
String aText = ScEditUtil::GetSpaceDelimitedString( *pEngine );
ScBaseCell* pNewCell = new ScStringCell( aText );
pNewCell->TakeBroadcaster( pOldCell->ReleaseBroadcaster() );
pNewCell->TakeNote( pOldCell->ReleaseNote() );
pItems[i].pCell = pNewCell;
delete pOldCell;
}
}
delete pEngine;
}
void ScColumn::RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow )
{
ScFieldEditEngine* pEngine = NULL;
SCSIZE i;
Search( nStartRow, i );
for (; i<nCount && pItems[i].nRow <= nEndRow; i++)
if ( pItems[i].pCell->GetCellType() == CELLTYPE_EDIT )
{
ScEditCell* pOldCell = (ScEditCell*) pItems[i].pCell;
const EditTextObject* pData = pOldCell->GetData();
// Fuer den Test auf harte Formatierung (ScEditAttrTester) sind die Defaults
// in der EditEngine unwichtig. Wenn der Tester spaeter einmal gleiche
// Attribute in Default und harter Formatierung erkennen und weglassen sollte,
// muessten an der EditEngine zu jeder Zelle die richtigen Defaults gesetzt
// werden!
// auf Attribute testen
if ( !pEngine )
{
//pEngine = new ScTabEditEngine(pDocument);
pEngine = new ScFieldEditEngine( pDocument->GetEditPool() );
// EE_CNTRL_ONLINESPELLING falls schon Fehler drin sind
pEngine->SetControlWord( pEngine->GetControlWord() | EE_CNTRL_ONLINESPELLING );
pDocument->ApplyAsianEditSettings( *pEngine );
}
pEngine->SetText( *pData );
sal_uInt16 nParCount = pEngine->GetParagraphCount();
for (sal_uInt16 nPar=0; nPar<nParCount; nPar++)
{
pEngine->QuickRemoveCharAttribs( nPar );
const SfxItemSet& rOld = pEngine->GetParaAttribs( nPar );
if ( rOld.Count() )
{
SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // leer
pEngine->SetParaAttribs( nPar, aNew );
}
}
// URL-Felder in Text wandeln (andere gibt's nicht, darum pType=0)
pEngine->RemoveFields( sal_True );
sal_Bool bSpellErrors = pEngine->HasOnlineSpellErrors();
sal_Bool bNeedObject = bSpellErrors || nParCount>1; // Errors/Absaetze behalten
// ScEditAttrTester nicht mehr noetig, Felder sind raus
if ( bNeedObject ) // bleibt Edit-Zelle
{
sal_uLong nCtrl = pEngine->GetControlWord();
sal_uLong nWantBig = bSpellErrors ? EE_CNTRL_ALLOWBIGOBJS : 0;
if ( ( nCtrl & EE_CNTRL_ALLOWBIGOBJS ) != nWantBig )
pEngine->SetControlWord( (nCtrl & ~EE_CNTRL_ALLOWBIGOBJS) | nWantBig );
EditTextObject* pNewData = pEngine->CreateTextObject();
pOldCell->SetData( pNewData, pEngine->GetEditTextObjectPool() );
delete pNewData;
}
else // String erzeugen
{
String aText = ScEditUtil::GetSpaceDelimitedString( *pEngine );
ScBaseCell* pNewCell = new ScStringCell( aText );
pNewCell->TakeBroadcaster( pOldCell->ReleaseBroadcaster() );
pNewCell->TakeNote( pOldCell->ReleaseNote() );
pItems[i].pCell = pNewCell;
delete pOldCell;
}
}
delete pEngine;
}
// =========================================================================================
sal_Bool ScColumn::TestTabRefAbs(SCTAB nTable)
{
sal_Bool bRet = sal_False;
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
if ( pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA )
if (((ScFormulaCell*)pItems[i].pCell)->TestTabRefAbs(nTable))
bRet = sal_True;
return bRet;
}
// =========================================================================================
ScColumnIterator::ScColumnIterator( const ScColumn* pCol, SCROW nStart, SCROW nEnd ) :
pColumn( pCol ),
nTop( nStart ),
nBottom( nEnd )
{
pColumn->Search( nTop, nPos );
}
ScColumnIterator::~ScColumnIterator()
{
}
sal_Bool ScColumnIterator::Next( SCROW& rRow, ScBaseCell*& rpCell )
{
if ( nPos < pColumn->nCount )
{
rRow = pColumn->pItems[nPos].nRow;
if ( rRow <= nBottom )
{
rpCell = pColumn->pItems[nPos].pCell;
++nPos;
return sal_True;
}
}
rRow = 0;
rpCell = NULL;
return sal_False;
}
SCSIZE ScColumnIterator::GetIndex() const // Index zur letzen abgefragten Zelle
{
return nPos - 1; // bei Next ist Pos hochgezaehlt worden
}
// -----------------------------------------------------------------------------------------
ScMarkedDataIter::ScMarkedDataIter( const ScColumn* pCol, const ScMarkData* pMarkData,
sal_Bool bAllIfNone ) :
pColumn( pCol ),
pMarkIter( NULL ),
bNext( sal_True ),
bAll( bAllIfNone )
{
if (pMarkData && pMarkData->IsMultiMarked())
pMarkIter = new ScMarkArrayIter( pMarkData->GetArray() + pCol->GetCol() );
}
ScMarkedDataIter::~ScMarkedDataIter()
{
delete pMarkIter;
}
sal_Bool ScMarkedDataIter::Next( SCSIZE& rIndex )
{
sal_Bool bFound = sal_False;
do
{
if (bNext)
{
if (!pMarkIter || !pMarkIter->Next( nTop, nBottom ))
{
if (bAll) // ganze Spalte
{
nTop = 0;
nBottom = MAXROW;
}
else
return sal_False;
}
pColumn->Search( nTop, nPos );
bNext = sal_False;
bAll = sal_False; // nur beim ersten Versuch
}
if ( nPos >= pColumn->nCount )
return sal_False;
if ( pColumn->pItems[nPos].nRow <= nBottom )
bFound = sal_True;
else
bNext = sal_True;
}
while (!bFound);
rIndex = nPos++;
return sal_True;
}
//UNUSED2009-05 sal_uInt16 ScColumn::GetErrorData( SCROW nRow ) const
//UNUSED2009-05 {
//UNUSED2009-05 SCSIZE nIndex;
//UNUSED2009-05 if (Search(nRow, nIndex))
//UNUSED2009-05 {
//UNUSED2009-05 ScBaseCell* pCell = pItems[nIndex].pCell;
//UNUSED2009-05 switch (pCell->GetCellType())
//UNUSED2009-05 {
//UNUSED2009-05 case CELLTYPE_FORMULA :
//UNUSED2009-05 return ((ScFormulaCell*)pCell)->GetErrCode();
//UNUSED2009-05 // break;
//UNUSED2009-05 default:
//UNUSED2009-05 return 0;
//UNUSED2009-05 }
//UNUSED2009-05 }
//UNUSED2009-05 return 0;
//UNUSED2009-05 }
//------------
sal_Bool ScColumn::IsEmptyData() const
{
return (nCount == 0);
}
sal_Bool ScColumn::IsEmptyVisData(sal_Bool bNotes) const
{
if (!pItems || nCount == 0)
return sal_True;
else
{
sal_Bool bVisData = sal_False;
SCSIZE i;
for (i=0; i<nCount && !bVisData; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() != CELLTYPE_NOTE || (bNotes && pCell->HasNote()) )
bVisData = sal_True;
}
return !bVisData;
}
}
SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const
{
// Notizen werden nicht mitgezaehlt
SCSIZE nVisCount = 0;
SCSIZE nIndex;
Search( nStartRow, nIndex );
while ( nIndex < nCount && pItems[nIndex].nRow <= nEndRow )
{
if ( pItems[nIndex].nRow >= nStartRow &&
pItems[nIndex].pCell->GetCellType() != CELLTYPE_NOTE )
{
++nVisCount;
}
++nIndex;
}
return nVisCount;
}
SCROW ScColumn::GetLastVisDataPos(sal_Bool bNotes) const
{
SCROW nRet = 0;
if (pItems)
{
SCSIZE i;
sal_Bool bFound = sal_False;
for (i=nCount; i>0 && !bFound; )
{
--i;
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() != CELLTYPE_NOTE || (bNotes && pCell->HasNote()) )
{
bFound = sal_True;
nRet = pItems[i].nRow;
}
}
}
return nRet;
}
SCROW ScColumn::GetFirstVisDataPos(sal_Bool bNotes) const
{
SCROW nRet = 0;
if (pItems)
{
SCSIZE i;
sal_Bool bFound = sal_False;
for (i=0; i<nCount && !bFound; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() != CELLTYPE_NOTE || (bNotes && pCell->HasNote()) )
{
bFound = sal_True;
nRet = pItems[i].nRow;
}
}
}
return nRet;
}
sal_Bool ScColumn::HasVisibleDataAt(SCROW nRow) const
{
SCSIZE nIndex;
if (Search(nRow, nIndex))
if (!pItems[nIndex].pCell->IsBlank())
return sal_True;
return sal_False;
}
sal_Bool ScColumn::IsEmptyAttr() const
{
if (pAttrArray)
return pAttrArray->IsEmpty();
else
return sal_True;
}
sal_Bool ScColumn::IsEmpty() const
{
return (IsEmptyData() && IsEmptyAttr());
}
sal_Bool ScColumn::IsEmptyBlock(SCROW nStartRow, SCROW nEndRow, bool bIgnoreNotes) const
{
if ( nCount == 0 || !pItems )
return sal_True;
SCSIZE nIndex;
Search( nStartRow, nIndex );
while ( nIndex < nCount && pItems[nIndex].nRow <= nEndRow )
{
if ( !pItems[nIndex].pCell->IsBlank( bIgnoreNotes ) ) // found a cell
return sal_False; // not empty
++nIndex;
}
return sal_True; // no cell found
}
SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const
{
SCSIZE nLines = 0;
sal_Bool bFound = sal_False;
SCSIZE i;
if (pItems && (nCount > 0))
{
if (eDir == DIR_BOTTOM)
{
i = nCount;
while (!bFound && (i > 0))
{
i--;
if ( pItems[i].nRow < nStartRow )
break;
bFound = pItems[i].nRow <= nEndRow && !pItems[i].pCell->IsBlank();
}
if (bFound)
nLines = static_cast<SCSIZE>(nEndRow - pItems[i].nRow);
else
nLines = static_cast<SCSIZE>(nEndRow - nStartRow);
}
else if (eDir == DIR_TOP)
{
i = 0;
while (!bFound && (i < nCount))
{
if ( pItems[i].nRow > nEndRow )
break;
bFound = pItems[i].nRow >= nStartRow && !pItems[i].pCell->IsBlank();
i++;
}
if (bFound)
nLines = static_cast<SCSIZE>(pItems[i-1].nRow - nStartRow);
else
nLines = static_cast<SCSIZE>(nEndRow - nStartRow);
}
}
else
nLines = static_cast<SCSIZE>(nEndRow - nStartRow);
return nLines;
}
SCROW ScColumn::GetFirstDataPos() const
{
if (nCount)
return pItems[0].nRow;
else
return 0;
}
SCROW ScColumn::GetLastDataPos() const
{
if (nCount)
return pItems[nCount-1].nRow;
else
return 0;
}
sal_Bool ScColumn::GetPrevDataPos(SCROW& rRow) const
{
sal_Bool bFound = sal_False;
SCSIZE i = nCount;
while (!bFound && (i > 0))
{
--i;
bFound = (pItems[i].nRow < rRow);
if (bFound)
rRow = pItems[i].nRow;
}
return bFound;
}
sal_Bool ScColumn::GetNextDataPos(SCROW& rRow) const // greater than rRow
{
SCSIZE nIndex;
if (Search( rRow, nIndex ))
++nIndex; // next cell
sal_Bool bMore = ( nIndex < nCount );
if ( bMore )
rRow = pItems[nIndex].nRow;
return bMore;
}
void ScColumn::FindDataAreaPos(SCROW& rRow, long nMovY) const
{
if (!nMovY) return;
sal_Bool bForward = (nMovY>0);
SCSIZE nIndex;
sal_Bool bThere = Search(rRow, nIndex);
if (bThere && pItems[nIndex].pCell->IsBlank())
bThere = sal_False;
if (bThere)
{
SCROW nLast = rRow;
SCSIZE nOldIndex = nIndex;
if (bForward)
{
if (nIndex<nCount-1)
{
++nIndex;
while (nIndex<nCount-1 && pItems[nIndex].nRow==nLast+1
&& !pItems[nIndex].pCell->IsBlank())
{
++nIndex;
++nLast;
}
if (nIndex==nCount-1)
if (pItems[nIndex].nRow==nLast+1 && !pItems[nIndex].pCell->IsBlank())
++nLast;
}
}
else
{
if (nIndex>0)
{
--nIndex;
while (nIndex>0 && pItems[nIndex].nRow+1==nLast
&& !pItems[nIndex].pCell->IsBlank())
{
--nIndex;
--nLast;
}
if (nIndex==0)
if (pItems[nIndex].nRow+1==nLast && !pItems[nIndex].pCell->IsBlank())
--nLast;
}
}
if (nLast==rRow)
{
bThere = sal_False;
nIndex = bForward ? nOldIndex+1 : nOldIndex;
}
else
rRow = nLast;
}
if (!bThere)
{
if (bForward)
{
while (nIndex<nCount && pItems[nIndex].pCell->IsBlank())
++nIndex;
if (nIndex<nCount)
rRow = pItems[nIndex].nRow;
else
rRow = MAXROW;
}
else
{
while (nIndex>0 && pItems[nIndex-1].pCell->IsBlank())
--nIndex;
if (nIndex>0)
rRow = pItems[nIndex-1].nRow;
else
rRow = 0;
}
}
}
sal_Bool ScColumn::HasDataAt(SCROW nRow) const
{
/* SCSIZE nIndex;
return Search( nRow, nIndex );
*/
// immer nur sichtbare interessant ?
//! dann HasVisibleDataAt raus
SCSIZE nIndex;
if (Search(nRow, nIndex))
if (!pItems[nIndex].pCell->IsBlank())
return sal_True;
return sal_False;
}
sal_Bool ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
{
if (pAttrArray && rCol.pAttrArray)
return pAttrArray->IsAllEqual( *rCol.pAttrArray, nStartRow, nEndRow );
else
return !pAttrArray && !rCol.pAttrArray;
}
sal_Bool ScColumn::IsVisibleAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
{
if (pAttrArray && rCol.pAttrArray)
return pAttrArray->IsVisibleEqual( *rCol.pAttrArray, nStartRow, nEndRow );
else
return !pAttrArray && !rCol.pAttrArray;
}
sal_Bool ScColumn::GetFirstVisibleAttr( SCROW& rFirstRow ) const
{
if (pAttrArray)
return pAttrArray->GetFirstVisibleAttr( rFirstRow );
else
return sal_False;
}
sal_Bool ScColumn::GetLastVisibleAttr( SCROW& rLastRow ) const
{
if (pAttrArray)
{
// row of last cell is needed
SCROW nLastData = GetLastVisDataPos( sal_True ); // always including notes, 0 if none
return pAttrArray->GetLastVisibleAttr( rLastRow, nLastData );
}
else
return sal_False;
}
sal_Bool ScColumn::GetLastAttr( SCROW& rLastRow ) const
{
if ( pAttrArray )
{
// Row of last cell is needed, always including notes, 0 if none.
SCROW nLastData = GetLastVisDataPos( sal_True );
return pAttrArray->GetLastAttr( rLastRow, nLastData );
}
else
{
return sal_False;
}
}
sal_Bool ScColumn::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
{
if (pAttrArray)
return pAttrArray->HasVisibleAttrIn( nStartRow, nEndRow );
else
return sal_False;
}
void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, sal_Bool* pUsed ) const
{
SCROW nRow = 0;
SCSIZE nIndex;
Search( nStartRow, nIndex );
while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEndRow) : sal_False )
{
pUsed[nRow-nStartRow] = sal_True;
++nIndex;
}
}
void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
{
SvtBroadcaster* pBC = NULL;
ScBaseCell* pCell;
SCSIZE nIndex;
if (Search(nRow,nIndex))
{
pCell = pItems[nIndex].pCell;
pBC = pCell->GetBroadcaster();
}
else
{
pCell = new ScNoteCell;
Insert(nRow, pCell);
}
if (!pBC)
{
pBC = new SvtBroadcaster;
pCell->TakeBroadcaster(pBC);
}
rLst.StartListening(*pBC);
}
void ScColumn::MoveListeners( SvtBroadcaster& rSource, SCROW nDestRow )
{
SvtBroadcaster* pBC = NULL;
ScBaseCell* pCell;
SCSIZE nIndex;
if (Search(nDestRow,nIndex))
{
pCell = pItems[nIndex].pCell;
pBC = pCell->GetBroadcaster();
}
else
{
pCell = new ScNoteCell;
Insert(nDestRow, pCell);
}
if (!pBC)
{
pBC = new SvtBroadcaster;
pCell->TakeBroadcaster(pBC);
}
if (rSource.HasListeners())
{
SvtListenerIter aIter( rSource);
for (SvtListener* pLst = aIter.GoStart(); pLst; pLst = aIter.GoNext())
{
pLst->StartListening( *pBC);
pLst->EndListening( rSource);
}
}
}
void ScColumn::EndListening( SvtListener& rLst, SCROW nRow )
{
SCSIZE nIndex;
if (Search(nRow,nIndex))
{
ScBaseCell* pCell = pItems[nIndex].pCell;
SvtBroadcaster* pBC = pCell->GetBroadcaster();
if (pBC)
{
rLst.EndListening(*pBC);
if (!pBC->HasListeners())
{
if (pCell->IsBlank())
DeleteAtIndex(nIndex);
else
pCell->DeleteBroadcaster();
}
}
// else
// DBG_ERROR("ScColumn::EndListening - kein Broadcaster");
}
// else
// DBG_ERROR("ScColumn::EndListening - keine Zelle");
}
void ScColumn::CompileDBFormula()
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*) pCell)->CompileDBFormula();
}
}
void ScColumn::CompileDBFormula( sal_Bool bCreateFormulaString )
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*) pCell)->CompileDBFormula( bCreateFormulaString );
}
}
void ScColumn::CompileNameFormula( sal_Bool bCreateFormulaString )
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*) pCell)->CompileNameFormula( bCreateFormulaString );
}
}
void ScColumn::CompileColRowNameFormula()
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*) pCell)->CompileColRowNameFormula();
}
}
void lcl_UpdateSubTotal( ScFunctionData& rData, ScBaseCell* pCell )
{
double nValue = 0.0;
sal_Bool bVal = sal_False;
sal_Bool bCell = sal_True;
switch (pCell->GetCellType())
{
case CELLTYPE_VALUE:
nValue = ((ScValueCell*)pCell)->GetValue();
bVal = sal_True;
break;
case CELLTYPE_FORMULA:
{
if ( rData.eFunc != SUBTOTAL_FUNC_CNT2 ) // da interessiert's nicht
{
ScFormulaCell* pFC = (ScFormulaCell*)pCell;
if ( pFC->GetErrCode() )
{
if ( rData.eFunc != SUBTOTAL_FUNC_CNT ) // fuer Anzahl einfach weglassen
rData.bError = sal_True;
}
else if (pFC->IsValue())
{
nValue = pFC->GetValue();
bVal = sal_True;
}
// sonst Text
}
}
break;
case CELLTYPE_NOTE:
bCell = sal_False;
break;
// bei Strings nichts
default:
{
// added to avoid warnings
}
}
if (!rData.bError)
{
switch (rData.eFunc)
{
case SUBTOTAL_FUNC_SUM:
case SUBTOTAL_FUNC_AVE:
if (bVal)
{
++rData.nCount;
if (!SubTotal::SafePlus( rData.nVal, nValue ))
rData.bError = sal_True;
}
break;
case SUBTOTAL_FUNC_CNT: // nur Werte
if (bVal)
++rData.nCount;
break;
case SUBTOTAL_FUNC_CNT2: // alle
if (bCell)
++rData.nCount;
break;
case SUBTOTAL_FUNC_MAX:
if (bVal)
if (++rData.nCount == 1 || nValue > rData.nVal )
rData.nVal = nValue;
break;
case SUBTOTAL_FUNC_MIN:
if (bVal)
if (++rData.nCount == 1 || nValue < rData.nVal )
rData.nVal = nValue;
break;
default:
{
// added to avoid warnings
}
}
}
}
// Mehrfachselektion:
void ScColumn::UpdateSelectionFunction( const ScMarkData& rMark,
ScFunctionData& rData,
ScFlatBoolRowSegments& rHiddenRows,
sal_Bool bDoExclude, SCROW nExStartRow, SCROW nExEndRow )
{
SCSIZE nIndex;
ScMarkedDataIter aDataIter(this, &rMark, sal_False);
while (aDataIter.Next( nIndex ))
{
SCROW nRow = pItems[nIndex].nRow;
bool bRowHidden = rHiddenRows.getValue(nRow);
if ( !bRowHidden )
if ( !bDoExclude || nRow < nExStartRow || nRow > nExEndRow )
lcl_UpdateSubTotal( rData, pItems[nIndex].pCell );
}
}
// bei bNoMarked die Mehrfachselektion weglassen
void ScColumn::UpdateAreaFunction( ScFunctionData& rData,
ScFlatBoolRowSegments& rHiddenRows,
SCROW nStartRow, SCROW nEndRow )
{
SCSIZE nIndex;
Search( nStartRow, nIndex );
while ( nIndex<nCount && pItems[nIndex].nRow<=nEndRow )
{
SCROW nRow = pItems[nIndex].nRow;
bool bRowHidden = rHiddenRows.getValue(nRow);
if ( !bRowHidden )
lcl_UpdateSubTotal( rData, pItems[nIndex].pCell );
++nIndex;
}
}
sal_uLong ScColumn::GetWeightedCount() const
{
sal_uLong nTotal = 0;
// Notizen werden nicht gezaehlt
for (SCSIZE i=0; i<nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
switch ( pCell->GetCellType() )
{
case CELLTYPE_VALUE:
case CELLTYPE_STRING:
++nTotal;
break;
case CELLTYPE_FORMULA:
nTotal += 5 + ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen();
break;
case CELLTYPE_EDIT:
nTotal += 50;
break;
default:
{
// added to avoid warnings
}
}
}
return nTotal;
}
sal_uLong ScColumn::GetCodeCount() const
{
sal_uLong nCodeCount = 0;
for (SCSIZE i=0; i<nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
nCodeCount += ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen();
}
return nCodeCount;
}
SCSIZE ScColumn::GetPatternCount()
{
return this->pAttrArray ? this->pAttrArray->Count() : 0;
}
SCSIZE ScColumn::GetPatternCount( SCROW nRw1, SCROW nRw2 )
{
return this->pAttrArray ? this->pAttrArray->Count( nRw1, nRw2 ) : 0;
}
bool ScColumn::ReservedPatternCount( SCSIZE nReserved )
{
return this->pAttrArray ? this->pAttrArray->Reserve( nReserved ) : false;
}