1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <boost/scoped_ptr.hpp>
22 #include <mdds/flat_segment_tree.hpp>
24 #include <sfx2/objsh.hxx>
25 #include <svl/zforlist.hxx>
26 #include <svl/zformat.hxx>
27 #include <svl/broadcast.hxx>
29 #include "scitems.hxx"
32 #include "formulacell.hxx"
33 #include "document.hxx"
34 #include "attarray.hxx"
35 #include "patattr.hxx"
36 #include "cellform.hxx"
37 #include "typedstrdata.hxx"
38 #include "formula/errorcodes.hxx"
39 #include "formula/token.hxx"
41 #include "docoptio.hxx" // GetStdPrecision for GetMaxNumberStringLen
42 #include "subtotal.hxx"
43 #include "markdata.hxx"
44 #include "detfunc.hxx" // For Notes for DeleteRange
46 #include "stringutil.hxx"
47 #include "docpool.hxx"
48 #include "globalnames.hxx"
49 #include "cellvalue.hxx"
50 #include "tokenarray.hxx"
51 #include "stlalgorithm.hxx"
52 #include "clipcontext.hxx"
54 #include <com/sun/star/i18n/LocaleDataItem.hpp>
58 using ::com::sun::star::i18n::LocaleDataItem
;
61 extern const ScFormulaCell
* pLastFormulaTreeTop
; // in cellform.cxx
62 using namespace formula
;
63 // STATIC DATA -----------------------------------------------------------
67 void broadcastCells(ScDocument
& rDoc
, SCCOL nCol
, SCROW nTab
, const std::vector
<SCROW
>& rRows
)
69 // Broadcast the changes.
70 ScHint
aHint(SC_HINT_DATACHANGED
, ScAddress(nCol
, 0, nTab
));
71 std::vector
<SCROW
>::const_iterator itRow
= rRows
.begin(), itRowEnd
= rRows
.end();
72 for (; itRow
!= itRowEnd
; ++itRow
)
74 aHint
.GetAddress().SetRow(*itRow
);
75 rDoc
.Broadcast(aHint
);
81 void ScColumn::Insert( sc::ColumnBlockPosition
& rBlockPos
, SCROW nRow
, ScBaseCell
* pNewCell
)
83 SetCell(rBlockPos
, nRow
, pNewCell
);
84 PostSetCell(nRow
, pNewCell
);
87 void ScColumn::Insert( SCROW nRow
, ScBaseCell
* pNewCell
)
89 SetCell(nRow
, pNewCell
);
90 PostSetCell(nRow
, pNewCell
);
94 void ScColumn::Insert( SCROW nRow
, sal_uInt32 nNumberFormat
, ScBaseCell
* pCell
)
97 SetNumberFormat(nRow
, nNumberFormat
);
100 void ScColumn::Append( sc::ColumnBlockPosition
& rBlockPos
, SCROW nRow
, ScBaseCell
* pCell
)
102 maItems
.push_back(ColEntry());
103 maItems
.back().pCell
= pCell
;
104 maItems
.back().nRow
= nRow
;
106 rBlockPos
.miCellTextAttrPos
=
107 maCellTextAttrs
.set(rBlockPos
.miCellTextAttrPos
, nRow
, sc::CellTextAttr());
109 CellStorageModified();
112 void ScColumn::Append( SCROW nRow
, ScBaseCell
* pCell
)
114 maItems
.push_back(ColEntry());
115 maItems
.back().pCell
= pCell
;
116 maItems
.back().nRow
= nRow
;
118 maCellTextAttrs
.set
<sc::CellTextAttr
>(nRow
, sc::CellTextAttr());
119 CellStorageModified();
122 void ScColumn::Delete( SCROW nRow
)
125 if (!Search(nRow
, nIndex
))
128 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
129 maItems
.erase(maItems
.begin() + nIndex
);
130 maCellTextAttrs
.set_empty(nRow
, nRow
);
131 // Should we free memory here (delta)? It'll be slower!
132 if (pCell
->GetCellType() == CELLTYPE_FORMULA
)
133 static_cast<ScFormulaCell
*>(pCell
)->EndListeningTo(pDocument
);
136 pDocument
->Broadcast(
137 ScHint(SC_HINT_DATACHANGED
, ScAddress(nCol
, nRow
, nTab
)));
139 CellStorageModified();
143 void ScColumn::DeleteAtIndex( SCSIZE nIndex
)
145 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
146 SCROW nRow
= maItems
[nIndex
].nRow
;
147 maItems
.erase(maItems
.begin() + nIndex
);
148 if (pCell
->GetCellType() == CELLTYPE_FORMULA
)
149 static_cast<ScFormulaCell
*>(pCell
)->EndListeningTo(pDocument
);
152 pDocument
->Broadcast(
153 ScHint(SC_HINT_DATACHANGED
, ScAddress(nCol
, nRow
, nTab
)));
155 maCellTextAttrs
.set_empty(nRow
, nRow
);
156 CellStorageModified();
160 void ScColumn::FreeAll()
162 for (SCSIZE i
= 0; i
< maItems
.size(); i
++)
163 maItems
[i
].pCell
->Delete();
166 // Text width should keep a logical empty range of 0-MAXROW when the cell array is empty.
167 maCellTextAttrs
.clear();
168 maCellTextAttrs
.resize(MAXROWCOUNT
);
169 CellStorageModified();
173 void ScColumn::DeleteRow( SCROW nStartRow
, SCSIZE nSize
)
175 SCROW nEndRow
= nStartRow
+ nSize
- 1;
177 pAttrArray
->DeleteRow( nStartRow
, nSize
);
179 maBroadcasters
.erase(nStartRow
, nEndRow
);
180 maBroadcasters
.resize(MAXROWCOUNT
);
182 if ( maItems
.empty() )
186 Search( nStartRow
, nFirstIndex
);
187 if ( nFirstIndex
>= maItems
.size() )
190 sal_Bool bOldAutoCalc
= pDocument
->GetAutoCalc();
191 pDocument
->SetAutoCalc( false ); // Avoid calculating it multiple times
194 SCSIZE nStartIndex
= 0;
195 SCSIZE nEndIndex
= 0;
199 for ( i
= nFirstIndex
; i
< maItems
.size() && maItems
[i
].nRow
<= nEndRow
; i
++ )
211 std::vector
<SCROW
> aDeletedRows
;
212 DeleteRange(nStartIndex
, nEndIndex
, IDF_CONTENTS
, aDeletedRows
);
213 broadcastCells(*pDocument
, nCol
, nTab
, aDeletedRows
);
215 Search( nStartRow
, i
);
216 if ( i
>= maItems
.size() )
218 pDocument
->SetAutoCalc( bOldAutoCalc
);
225 // There are cells below the deletion point. Shift their row positions.
227 // Shift the text width array too (before the broadcast).
228 maCellTextAttrs
.erase(nStartRow
, nEndRow
);
229 maCellTextAttrs
.resize(MAXROWCOUNT
);
231 ScAddress
aAdr( nCol
, 0, nTab
);
232 ScHint
aHint(SC_HINT_DATACHANGED
, aAdr
); // only areas (ScBaseCell* == NULL)
233 ScAddress
& rAddress
= aHint
.GetAddress();
234 // for sparse occupation use single broadcasts, not ranges
235 bool bSingleBroadcasts
= (((maItems
.back().nRow
- maItems
[i
].nRow
) /
236 (maItems
.size() - i
)) > 1);
237 if ( bSingleBroadcasts
)
239 SCROW nLastBroadcast
= MAXROW
+1;
240 for ( ; i
< maItems
.size(); i
++ )
242 SCROW nOldRow
= maItems
[i
].nRow
;
243 // Broadcast change in source
244 rAddress
.SetRow( nOldRow
);
245 pDocument
->AreaBroadcast( aHint
);
246 SCROW nNewRow
= (maItems
[i
].nRow
-= nSize
);
247 // Broadcast change in target
248 if ( nLastBroadcast
!= nNewRow
)
249 { // Do not broadcast successive ones
250 rAddress
.SetRow( nNewRow
);
251 pDocument
->AreaBroadcast( aHint
);
253 nLastBroadcast
= nOldRow
;
254 ScBaseCell
* pCell
= maItems
[i
].pCell
;
255 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
256 ((ScFormulaCell
*)pCell
)->aPos
.SetRow( nNewRow
);
261 rAddress
.SetRow( maItems
[i
].nRow
);
262 ScRange
aRange( rAddress
);
263 aRange
.aEnd
.SetRow( maItems
.back().nRow
);
264 for ( ; i
< maItems
.size(); i
++ )
266 SCROW nNewRow
= (maItems
[i
].nRow
-= nSize
);
267 ScBaseCell
* pCell
= maItems
[i
].pCell
;
268 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
269 ((ScFormulaCell
*)pCell
)->aPos
.SetRow( nNewRow
);
271 pDocument
->AreaBroadcastInRange( aRange
, aHint
);
274 CellStorageModified();
275 pDocument
->SetAutoCalc( bOldAutoCalc
);
278 void ScColumn::UpdateScriptType( sc::CellTextAttr
& rAttr
, SCROW nRow
)
280 if (rAttr
.mnScriptType
!= SC_SCRIPTTYPE_UNKNOWN
)
281 // Already updated. Nothing to do.
284 // Script type not yet determined. Determine the real script
285 // type, and store it.
286 const ScPatternAttr
* pPattern
= GetPattern(nRow
);
290 ScRefCellValue aCell
;
291 ScAddress
aPos(nCol
, nRow
, nTab
);
292 aCell
.assign(*pDocument
, aPos
);
294 const SfxItemSet
* pCondSet
= NULL
;
295 ScConditionalFormatList
* pCFList
= pDocument
->GetCondFormList(nTab
);
298 const ScCondFormatItem
& rItem
=
299 static_cast<const ScCondFormatItem
&>(pPattern
->GetItem(ATTR_CONDITIONAL
));
300 const std::vector
<sal_uInt32
>& rData
= rItem
.GetCondFormatData();
301 pCondSet
= pDocument
->GetCondResult(aCell
, aPos
, *pCFList
, rData
);
304 SvNumberFormatter
* pFormatter
= pDocument
->GetFormatTable();
308 sal_uLong nFormat
= pPattern
->GetNumberFormat(pFormatter
, pCondSet
);
309 ScCellFormat::GetString(aCell
, nFormat
, aStr
, &pColor
, *pFormatter
, pDocument
);
311 // Store the real script type to the array.
312 rAttr
.mnScriptType
= pDocument
->GetStringScriptType(aStr
);
317 bool isDate(const ScDocument
& rDoc
, const ScColumn
& rCol
, SCROW nRow
)
319 sal_uLong nIndex
= (sal_uLong
)((SfxUInt32Item
*)rCol
.GetAttr(nRow
, ATTR_VALUE_FORMAT
))->GetValue();
320 short nType
= rDoc
.GetFormatTable()->GetType(nIndex
);
321 return (nType
== NUMBERFORMAT_DATE
) || (nType
== NUMBERFORMAT_TIME
) || (nType
== NUMBERFORMAT_DATETIME
);
324 bool checkDeleteCellByFlag(
325 CellType eCellType
, sal_uInt16 nDelFlag
, const ScDocument
& rDoc
, const ScColumn
& rCol
, const ColEntry
& rEntry
)
327 bool bDelete
= false;
333 sal_uInt16 nValFlags
= nDelFlag
& (IDF_DATETIME
|IDF_VALUE
);
334 // delete values and dates?
335 bDelete
= nValFlags
== (IDF_DATETIME
|IDF_VALUE
);
336 // if not, decide according to cell number format
337 if (!bDelete
&& (nValFlags
!= 0))
339 bool bIsDate
= isDate(rDoc
, rCol
, rEntry
.nRow
);
340 bDelete
= nValFlags
== (bIsDate
? IDF_DATETIME
: IDF_VALUE
);
344 case CELLTYPE_STRING
:
346 bDelete
= (nDelFlag
& IDF_STRING
) != 0;
348 case CELLTYPE_FORMULA
:
349 bDelete
= (nDelFlag
& IDF_FORMULA
) != 0;
351 default:; // added to avoid warnings
359 void ScColumn::DeleteRange(
360 SCSIZE nStartIndex
, SCSIZE nEndIndex
, sal_uInt16 nDelFlag
, std::vector
<SCROW
>& rDeletedRows
)
362 /* If caller specifies to not remove the note caption objects, all cells
363 have to forget the pointers to them. This is used e.g. while undoing a
364 "paste cells" operation, which removes the caption objects later in
367 // cache all formula cells, they will be deleted at end of this function
368 std::vector
<ScFormulaCell
*> aDelCells
;
369 aDelCells
.reserve( nEndIndex
- nStartIndex
+ 1 );
371 typedef mdds::flat_segment_tree
<SCSIZE
, bool> RemovedSegments_t
;
372 RemovedSegments_t
aRemovedSegments(nStartIndex
, maItems
.size(), false);
373 SCSIZE nFirst
= nStartIndex
;
375 for ( SCSIZE nIdx
= nStartIndex
; nIdx
<= nEndIndex
; ++nIdx
)
377 if (((nDelFlag
& IDF_CONTENTS
) == IDF_CONTENTS
))
379 // all content is to be deleted.
381 ScBaseCell
* pOldCell
= maItems
[ nIdx
].pCell
;
382 rDeletedRows
.push_back(maItems
[nIdx
].nRow
);
384 if (pOldCell
->GetCellType() == CELLTYPE_FORMULA
)
386 // cache formula cell, will be deleted below
387 aDelCells
.push_back( static_cast< ScFormulaCell
* >( pOldCell
) );
395 // delete some contents of the cells, or cells with broadcaster
396 bool bDelete
= false;
397 ScBaseCell
* pOldCell
= maItems
[nIdx
].pCell
;
398 CellType eCellType
= pOldCell
->GetCellType();
399 if ((nDelFlag
& IDF_CONTENTS
) == IDF_CONTENTS
)
400 // All cell types to be deleted.
404 // Decide whether to delete the cell object according to passed
406 bDelete
= checkDeleteCellByFlag(eCellType
, nDelFlag
, *pDocument
, *this, maItems
[nIdx
]);
411 // remove cell entry in cell item list
412 if (eCellType
== CELLTYPE_FORMULA
)
414 // Cache formula cells (will be deleted later), delete cell of other type.
415 aDelCells
.push_back(static_cast<ScFormulaCell
*>(pOldCell
));
420 rDeletedRows
.push_back(maItems
[nIdx
].nRow
);
425 // We just came to a non-deleted cell after a segment of
426 // deleted ones. So we need to remember the segment
429 aRemovedSegments
.insert_back(nFirst
, nIdx
, true);
433 // there is a segment of deleted cells at the end
434 if (nFirst
<= nEndIndex
)
435 aRemovedSegments
.insert_back(nFirst
, nEndIndex
+ 1, true);
438 // Remove segments from the column array, containing pDummyCell and
439 // formula cell pointers to be deleted.
441 RemovedSegments_t::const_reverse_iterator it
= aRemovedSegments
.rbegin();
442 RemovedSegments_t::const_reverse_iterator itEnd
= aRemovedSegments
.rend();
444 std::vector
<ColEntry
>::iterator itErase
, itEraseEnd
;
445 SCSIZE nEndSegment
= it
->first
; // should equal maItems.size(). Non-inclusive.
446 // Skip the first node.
447 for (++it
; it
!= itEnd
; ++it
)
451 // Don't remove this segment.
452 nEndSegment
= it
->first
;
456 // Remove this segment.
457 SCSIZE nStartSegment
= it
->first
;
458 SCROW nStartRow
= maItems
[nStartSegment
].nRow
;
459 SCROW nEndRow
= maItems
[nEndSegment
-1].nRow
;
461 itErase
= maItems
.begin();
462 std::advance(itErase
, nStartSegment
);
463 itEraseEnd
= maItems
.begin();
464 std::advance(itEraseEnd
, nEndSegment
);
465 maItems
.erase(itErase
, itEraseEnd
);
467 maCellTextAttrs
.set_empty(nStartRow
, nEndRow
);
469 nEndSegment
= nStartSegment
;
473 pDocument
->EndListeningFormulaCells(aDelCells
);
474 std::for_each(aDelCells
.begin(), aDelCells
.end(), ScDeleteObjectByPtr
<ScFormulaCell
>());
477 void ScColumn::DeleteArea(SCROW nStartRow
, SCROW nEndRow
, sal_uInt16 nDelFlag
)
479 // FreeAll must not be called here due to Broadcasters
480 // Delete attribute at the end so that we can distinguish between numbers and dates
482 sal_uInt16 nContMask
= IDF_CONTENTS
;
483 // IDF_NOCAPTIONS needs to be passed too, if IDF_NOTE is set
484 if( nDelFlag
& IDF_NOTE
)
485 nContMask
|= IDF_NOCAPTIONS
;
486 sal_uInt16 nContFlag
= nDelFlag
& nContMask
;
488 std::vector
<SCROW
> aDeletedRows
;
490 if ( !maItems
.empty() && nContFlag
)
492 if (nStartRow
==0 && nEndRow
==MAXROW
)
494 DeleteRange(0, maItems
.size()-1, nContFlag
, aDeletedRows
);
498 sal_Bool bFound
=false;
499 SCSIZE nStartIndex
= 0;
500 SCSIZE nEndIndex
= 0;
501 for (SCSIZE i
= 0; i
< maItems
.size(); i
++)
502 if ((maItems
[i
].nRow
>= nStartRow
) && (maItems
[i
].nRow
<= nEndRow
))
512 DeleteRange(nStartIndex
, nEndIndex
, nContFlag
, aDeletedRows
);
516 if ( nDelFlag
& IDF_EDITATTR
)
518 OSL_ENSURE( nContFlag
== 0, "DeleteArea: Wrong Flags" );
519 RemoveEditAttribs( nStartRow
, nEndRow
);
522 // Delete attributes just now
523 if ((nDelFlag
& IDF_ATTRIB
) == IDF_ATTRIB
)
524 pAttrArray
->DeleteArea( nStartRow
, nEndRow
);
525 else if ((nDelFlag
& IDF_HARDATTR
) == IDF_HARDATTR
)
526 pAttrArray
->DeleteHardAttr( nStartRow
, nEndRow
);
528 // Broadcast on only cells that were deleted; no point broadcasting on
529 // cells that were already empty before the deletion.
530 broadcastCells(*pDocument
, nCol
, nTab
, aDeletedRows
);
534 ScFormulaCell
* ScColumn::CreateRefCell( ScDocument
* pDestDoc
, const ScAddress
& rDestPos
,
535 SCSIZE nIndex
, sal_uInt16 nFlags
) const
537 sal_uInt16 nContFlags
= nFlags
& IDF_CONTENTS
;
541 // Test whether the Cell should be copied
542 // Also do this for IDF_CONTENTS, due to Notes/Broadcasters
543 sal_Bool bMatch
= false;
544 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
545 CellType eCellType
= pCell
->GetCellType();
550 sal_uInt16 nValFlags
= nFlags
& (IDF_DATETIME
|IDF_VALUE
);
552 if ( nValFlags
== (IDF_DATETIME
|IDF_VALUE
) )
554 else if ( nValFlags
)
556 sal_uLong nNumIndex
= (sal_uLong
)((SfxUInt32Item
*)GetAttr(
557 maItems
[nIndex
].nRow
, ATTR_VALUE_FORMAT
))->GetValue();
558 short nTyp
= pDocument
->GetFormatTable()->GetType(nNumIndex
);
559 if ((nTyp
== NUMBERFORMAT_DATE
) || (nTyp
== NUMBERFORMAT_TIME
) || (nTyp
== NUMBERFORMAT_DATETIME
))
560 bMatch
= ((nFlags
& IDF_DATETIME
) != 0);
562 bMatch
= ((nFlags
& IDF_VALUE
) != 0);
566 case CELLTYPE_STRING
:
567 case CELLTYPE_EDIT
: bMatch
= ((nFlags
& IDF_STRING
) != 0); break;
568 case CELLTYPE_FORMULA
: bMatch
= ((nFlags
& IDF_FORMULA
) != 0); break;
571 // added to avoid warnings
579 ScSingleRefData aRef
;
581 aRef
.nRow
= maItems
[nIndex
].nRow
;
583 aRef
.InitFlags(); // -> Everything absolute
584 aRef
.SetFlag3D(true);
586 // 3D (false) and TabRel (true), if the final Position is at the same Table?
587 // The target position is not yet known for TransposeClip!
589 aRef
.CalcRelFromAbs( rDestPos
);
592 aArr
.AddSingleReference( aRef
);
594 return new ScFormulaCell( pDestDoc
, rDestPos
, &aArr
);
597 bool ScColumn::InitBlockPosition( sc::ColumnBlockPosition
& rBlockPos
)
599 rBlockPos
.miBroadcasterPos
= maBroadcasters
.begin();
600 rBlockPos
.miCellTextAttrPos
= maCellTextAttrs
.begin();
605 // nRow1, nRow2 = target position
607 void ScColumn::CopyFromClip(
608 sc::CopyFromClipContext
& rCxt
, SCROW nRow1
, SCROW nRow2
, long nDy
, ScColumn
& rColumn
)
610 if ((rCxt
.getInsertFlag() & IDF_ATTRIB
) != 0)
612 if (rCxt
.isSkipAttrForEmptyCells())
614 // copy only attributes for non-empty cells
615 // (notes are not counted as non-empty here, to match the content behavior)
618 rColumn
.Search( nRow1
-nDy
, nStartIndex
);
619 while ( nStartIndex
< rColumn
.maItems
.size() && rColumn
.maItems
[nStartIndex
].nRow
<= nRow2
-nDy
)
621 SCSIZE nEndIndex
= nStartIndex
;
622 SCROW nStartRow
= rColumn
.maItems
[nStartIndex
].nRow
;
623 SCROW nEndRow
= nStartRow
;
625 // find consecutive non-empty cells
626 while ( nEndRow
< nRow2
-nDy
&&
627 nEndIndex
+1 < rColumn
.maItems
.size() &&
628 rColumn
.maItems
[nEndIndex
+1].nRow
== nEndRow
+1 )
634 rColumn
.pAttrArray
->CopyAreaSafe( nStartRow
+nDy
, nEndRow
+nDy
, nDy
, *pAttrArray
);
635 nStartIndex
= nEndIndex
+ 1;
639 rColumn
.pAttrArray
->CopyAreaSafe( nRow1
, nRow2
, nDy
, *pAttrArray
);
641 if ((rCxt
.getInsertFlag() & IDF_CONTENTS
) == 0)
644 if (rCxt
.isAsLink() && rCxt
.getInsertFlag() == IDF_ALL
)
646 // We also reference empty cells for "ALL"
647 // IDF_ALL must always contain more flags when compared to "Insert contents" as
648 // contents can be selected one by one!
650 ReserveSize(maItems
.size() + static_cast<SCSIZE
>(nRow2
-nRow1
+1));
652 ScAddress
aDestPos( nCol
, 0, nTab
); // Adapt Row
654 // Create reference (Source Position)
655 ScSingleRefData aRef
;
656 aRef
.nCol
= rColumn
.nCol
;
658 aRef
.nTab
= rColumn
.nTab
;
659 aRef
.InitFlags(); // -> All absolute
660 aRef
.SetFlag3D(true);
662 for (SCROW nDestRow
= nRow1
; nDestRow
<= nRow2
; nDestRow
++)
664 aRef
.nRow
= nDestRow
- nDy
; // Source row
665 aDestPos
.SetRow( nDestRow
);
667 aRef
.CalcRelFromAbs( aDestPos
);
669 aArr
.AddSingleReference( aRef
);
670 Insert( nDestRow
, new ScFormulaCell( pDocument
, aDestPos
, &aArr
) );
676 SCSIZE nColCount
= rColumn
.maItems
.size();
678 // ignore IDF_FORMULA - "all contents but no formulas" results in the same number of cells
679 if ((rCxt
.getInsertFlag() & ( IDF_CONTENTS
& ~IDF_FORMULA
)) == ( IDF_CONTENTS
& ~IDF_FORMULA
) && nRow2
-nRow1
>= 64)
681 //! Always do the Resize from the outside, where the number of repetitions is known
682 //! (then it can be removed here)
684 ReserveSize(maItems
.size() + nColCount
);
687 sal_Bool bAtEnd
= false;
688 for (SCSIZE i
= 0; i
< nColCount
&& !bAtEnd
; i
++)
690 SCsROW nDestRow
= rColumn
.maItems
[i
].nRow
+ nDy
;
691 if ( nDestRow
> (SCsROW
) nRow2
)
693 else if ( nDestRow
>= (SCsROW
) nRow1
)
695 // rows at the beginning may be skipped if filtered rows are left out,
696 // nDestRow may be negative then
698 ScAddress
aDestPos( nCol
, (SCROW
)nDestRow
, nTab
);
700 ScBaseCell
* pNewCell
= rCxt
.isAsLink() ?
701 rColumn
.CreateRefCell(pDocument
, aDestPos
, i
, rCxt
.getInsertFlag()) :
702 rColumn
.CloneCell(i
, rCxt
.getInsertFlag(), *pDocument
, aDestPos
);
705 sc::ColumnBlockPosition
* p
= rCxt
.getBlockPosition(nTab
, nCol
);
707 Insert(*p
, aDestPos
.Row(), pNewCell
);
709 Insert(aDestPos
.Row(), pNewCell
);
719 * Helper for ScColumn::CloneCell
720 * Decide whether to clone a value cell depending on clone flags and number format.
722 bool lclCanCloneValue( ScDocument
& rDoc
, const ScColumn
& rCol
, SCROW nRow
, bool bCloneValue
, bool bCloneDateTime
)
724 // values and dates, or nothing to be cloned -> not needed to check number format
725 if( bCloneValue
== bCloneDateTime
)
728 // check number format of value cell
729 sal_uLong nNumIndex
= (sal_uLong
)((SfxUInt32Item
*)rCol
.GetAttr( nRow
, ATTR_VALUE_FORMAT
))->GetValue();
730 short nTyp
= rDoc
.GetFormatTable()->GetType( nNumIndex
);
731 bool bIsDateTime
= (nTyp
== NUMBERFORMAT_DATE
) || (nTyp
== NUMBERFORMAT_TIME
) || (nTyp
== NUMBERFORMAT_DATETIME
);
732 return bIsDateTime
? bCloneDateTime
: bCloneValue
;
738 ScBaseCell
* ScColumn::CloneCell(
739 SCSIZE nIndex
, sal_uInt16 nFlags
, ScDocument
& rDestDoc
, const ScAddress
& rDestPos
) const
741 bool bCloneValue
= (nFlags
& IDF_VALUE
) != 0;
742 bool bCloneDateTime
= (nFlags
& IDF_DATETIME
) != 0;
743 bool bCloneString
= (nFlags
& IDF_STRING
) != 0;
744 bool bCloneSpecialBoolean
= (nFlags
& IDF_SPECIAL_BOOLEAN
) != 0;
745 bool bCloneFormula
= (nFlags
& IDF_FORMULA
) != 0;
746 bool bForceFormula
= false;
748 ScBaseCell
* pNew
= 0;
749 ScBaseCell
& rSource
= *maItems
[nIndex
].pCell
;
750 switch (rSource
.GetCellType())
752 case CELLTYPE_STRING
:
754 // note will be cloned below
756 pNew
= rSource
.Clone( rDestDoc
, rDestPos
);
760 // note will be cloned below
761 if (lclCanCloneValue( *pDocument
, *this, maItems
[nIndex
].nRow
, bCloneValue
, bCloneDateTime
))
762 pNew
= rSource
.Clone( rDestDoc
, rDestPos
);
765 case CELLTYPE_FORMULA
:
766 if ( bCloneSpecialBoolean
)
768 ScFormulaCell
& rForm
= (ScFormulaCell
&)rSource
;
770 // FIXME: do we have a localisation issue here?
771 rForm
.GetFormula( aBuf
);
772 OUString
aVal( aBuf
.makeStringAndClear() );
773 if ( aVal
== "=TRUE()" || aVal
== "=FALSE()" )
774 bForceFormula
= true;
776 if (bForceFormula
|| bCloneFormula
)
778 // note will be cloned below
779 pNew
= rSource
.Clone( rDestDoc
, rDestPos
);
781 else if ( (bCloneValue
|| bCloneDateTime
|| bCloneString
) && !rDestDoc
.IsUndo() )
783 // Always just copy the original row to the Undo Documen;
784 // do not create Value/string cells from formulas
785 ScFormulaCell
& rForm
= (ScFormulaCell
&)rSource
;
786 sal_uInt16 nErr
= rForm
.GetErrCode();
789 // error codes are cloned with values
792 ScFormulaCell
* pErrCell
= new ScFormulaCell( &rDestDoc
, rDestPos
);
793 pErrCell
->SetErrCode( nErr
);
797 else if (rForm
.IsValue())
799 if (lclCanCloneValue( *pDocument
, *this, maItems
[nIndex
].nRow
, bCloneValue
, bCloneDateTime
))
801 double nVal
= rForm
.GetValue();
802 pNew
= new ScValueCell(nVal
);
805 else if (bCloneString
)
807 String aString
= rForm
.GetString();
808 // do not clone empty string
809 if (aString
.Len() > 0)
811 if ( rForm
.IsMultilineResult() )
813 pNew
= new ScEditCell( aString
, &rDestDoc
);
817 pNew
= new ScStringCell( aString
);
824 default: OSL_FAIL( "ScColumn::CloneCell - unknown cell type" );
831 void ScColumn::MixMarked(
832 sc::MixDocContext
& rCxt
, const ScMarkData
& rMark
, sal_uInt16 nFunction
,
833 bool bSkipEmpty
, const ScColumn
& rSrcCol
)
837 if (rMark
.IsMultiMarked())
839 ScMarkArrayIter
aIter( rMark
.GetArray()+nCol
);
840 while (aIter
.Next( nRow1
, nRow2
))
841 MixData(rCxt
, nRow1
, nRow2
, nFunction
, bSkipEmpty
, rSrcCol
);
848 bool lcl_DoFunction( double& rVal1
, double nVal2
, sal_uInt16 nFunction
)
854 bOk
= SubTotal::SafePlus( rVal1
, nVal2
);
857 nVal2
= -nVal2
; // FIXME: Can we do this alwyas without error?
858 bOk
= SubTotal::SafePlus( rVal1
, nVal2
);
861 bOk
= SubTotal::SafeMult( rVal1
, nVal2
);
864 bOk
= SubTotal::SafeDiv( rVal1
, nVal2
);
870 void lcl_AddCode( ScTokenArray
& rArr
, ScFormulaCell
* pCell
)
872 rArr
.AddOpCode(ocOpen
);
874 ScTokenArray
* pCode
= pCell
->GetCode();
877 const formula::FormulaToken
* pToken
= pCode
->First();
880 rArr
.AddToken( *pToken
);
881 pToken
= pCode
->Next();
885 rArr
.AddOpCode(ocClose
);
888 struct FindRemovedCell
: std::unary_function
<ColEntry
, bool>
890 bool operator() (const ColEntry
& rEntry
) const
892 return rEntry
.pCell
== NULL
;
898 void ScColumn::MixData(
899 sc::MixDocContext
& rCxt
, SCROW nRow1
, SCROW nRow2
, sal_uInt16 nFunction
,
900 bool bSkipEmpty
, const ScColumn
& rSrcCol
)
902 SCSIZE nSrcCount
= rSrcCol
.maItems
.size();
904 sc::ColumnBlockPosition
* p
= rCxt
.getBlockPosition(nTab
, nCol
);
907 Search( nRow1
, nIndex
);
909 SCSIZE nSrcIndex
= 0, nDestIndex
= 0;
910 rSrcCol
.Search( nRow1
, nSrcIndex
); // See if data is at the beginning
912 SCROW nNextThis
= MAXROW
+1;
913 if ( nIndex
< maItems
.size() )
914 nNextThis
= maItems
[nIndex
].nRow
;
915 SCROW nNextSrc
= MAXROW
+1;
916 if ( nSrcIndex
< nSrcCount
)
917 nNextSrc
= rSrcCol
.maItems
[nSrcIndex
].nRow
;
919 bool bDeferredDelete
= false;
920 while ( nNextThis
<= nRow2
|| nNextSrc
<= nRow2
)
922 SCROW nRow
= std::min( nNextThis
, nNextSrc
);
924 ScBaseCell
* pSrc
= NULL
;
925 ScBaseCell
* pDest
= NULL
;
926 ScBaseCell
* pNew
= NULL
;
927 bool bDelete
= false;
929 if ( nSrcIndex
< nSrcCount
&& nNextSrc
== nRow
)
930 pSrc
= rSrcCol
.maItems
[nSrcIndex
].pCell
;
932 if ( nIndex
< maItems
.size() && nNextThis
== nRow
)
934 pDest
= maItems
[nIndex
].pCell
;
938 OSL_ENSURE( pSrc
|| pDest
, "What happened?" );
940 CellType eSrcType
= pSrc
? pSrc
->GetCellType() : CELLTYPE_NONE
;
941 CellType eDestType
= pDest
? pDest
->GetCellType() : CELLTYPE_NONE
;
943 bool bSrcEmpty
= (eSrcType
== CELLTYPE_NONE
);
944 bool bDestEmpty
= (eDestType
== CELLTYPE_NONE
);
946 if ( bSkipEmpty
&& bDestEmpty
) // Restore original row
948 if ( pSrc
) // Did we have a row here?
950 pNew
= pSrc
->Clone( *pDocument
);
953 else if ( nFunction
) // Really provide calculation function
957 if ( eSrcType
== CELLTYPE_VALUE
)
958 nVal1
= ((ScValueCell
*)pSrc
)->GetValue();
961 if ( eDestType
== CELLTYPE_VALUE
)
962 nVal2
= ((ScValueCell
*)pDest
)->GetValue();
966 // Empty row is treated as a value
967 sal_Bool bSrcVal
= ( bSrcEmpty
|| eSrcType
== CELLTYPE_VALUE
);
968 sal_Bool bDestVal
= ( bDestEmpty
|| eDestType
== CELLTYPE_VALUE
);
970 sal_Bool bSrcText
= ( eSrcType
== CELLTYPE_STRING
||
971 eSrcType
== CELLTYPE_EDIT
);
972 sal_Bool bDestText
= ( eDestType
== CELLTYPE_STRING
||
973 eDestType
== CELLTYPE_EDIT
);
975 // Else we only have formulas ...
976 if ( bSrcEmpty
&& bDestEmpty
)
978 // Both empty -> do nothing
980 else if ( bSrcVal
&& bDestVal
)
982 // Insterted new value or both have overflown
983 sal_Bool bOk
= lcl_DoFunction( nVal1
, nVal2
, nFunction
);
986 pNew
= new ScValueCell( nVal1
);
989 ScFormulaCell
* pFC
= new ScFormulaCell( pDocument
,
990 ScAddress( nCol
, nRow
, nTab
) );
991 pFC
->SetErrCode( errNoValue
);
992 //! oder NOVALUE, dann auch in consoli,
993 //! sonst in Interpreter::GetCellValue die Abfrage auf errNoValue raus
994 //! (dann geht Stringzelle+Wertzelle nicht mehr)
998 else if ( bSrcText
|| bDestText
)
1000 // We do no not calculate with texts - alwyas "old" cell, thus pSrc
1002 pNew
= pSrc
->Clone( *pDocument
);
1008 // Combination of value and at least one formula -> Create formula
1012 if ( eSrcType
== CELLTYPE_FORMULA
)
1013 lcl_AddCode( aArr
, (ScFormulaCell
*)pSrc
);
1015 aArr
.AddDouble( nVal1
);
1019 switch ( nFunction
)
1021 case PASTE_ADD
: eOp
= ocAdd
; break;
1022 case PASTE_SUB
: eOp
= ocSub
; break;
1023 case PASTE_MUL
: eOp
= ocMul
; break;
1024 case PASTE_DIV
: eOp
= ocDiv
; break;
1026 aArr
.AddOpCode(eOp
); // Function
1029 if ( eDestType
== CELLTYPE_FORMULA
)
1030 lcl_AddCode( aArr
, (ScFormulaCell
*)pDest
);
1032 aArr
.AddDouble( nVal2
);
1034 pNew
= new ScFormulaCell( pDocument
, ScAddress( nCol
, nRow
, nTab
), &aArr
);
1039 if ( pNew
|| bDelete
) // New result?
1041 if (pDest
&& !pNew
) // Old cell present?
1043 // Delete the destination cell because the cell was originally
1044 // empty. Don't erase its slot in the cell array yet.
1045 OSL_ASSERT(pDest
== maItems
[nDestIndex
].pCell
);
1046 maItems
[nDestIndex
].pCell
= NULL
;
1048 if (pDest
->GetCellType() == CELLTYPE_FORMULA
)
1049 static_cast<ScFormulaCell
*>(pDest
)->EndListeningTo(pDocument
);
1052 bDeferredDelete
= true;
1057 Insert(*p
, nRow
, pNew
);
1059 Insert(nRow
, pNew
); // Insert new one
1062 Search( nRow
, nIndex
); // Everything could have moved
1064 nNextThis
= nRow
; // nIndex points right at nRow now
1066 nNextThis
= ( nIndex
< maItems
.size() ) ? maItems
[nIndex
].nRow
: MAXROW
+1;
1069 if ( nNextThis
== nRow
)
1072 nNextThis
= ( nIndex
< maItems
.size() ) ? maItems
[nIndex
].nRow
: MAXROW
+1;
1074 if ( nNextSrc
== nRow
)
1077 nNextSrc
= ( nSrcIndex
< nSrcCount
) ?
1078 rSrcCol
.maItems
[nSrcIndex
].nRow
:
1083 if (bDeferredDelete
)
1085 // Erase all the slots in the cell array where the deleted cells
1086 // previously occupied.
1087 std::vector
<ColEntry
>::iterator it
=
1088 std::remove_if(maItems
.begin(), maItems
.end(), FindRemovedCell());
1090 maItems
.erase(it
, maItems
.end());
1092 // Reset the cell text attriute array to keep it in sync again.
1093 ResetCellTextAttrs();
1098 ScAttrIterator
* ScColumn::CreateAttrIterator( SCROW nStartRow
, SCROW nEndRow
) const
1100 return new ScAttrIterator( pAttrArray
, nStartRow
, nEndRow
);
1104 void ScColumn::StartAllListeners()
1106 if (maItems
.empty())
1109 for (SCSIZE i
= 0; i
< maItems
.size(); i
++)
1111 ScBaseCell
* pCell
= maItems
[i
].pCell
;
1112 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
1114 SCROW nRow
= maItems
[i
].nRow
;
1115 ((ScFormulaCell
*)pCell
)->StartListeningTo( pDocument
);
1116 if ( nRow
!= maItems
[i
].nRow
)
1117 Search( nRow
, i
); // Insert Listener?
1123 void ScColumn::StartNeededListeners()
1125 if (maItems
.empty())
1128 for (SCSIZE i
= 0; i
< maItems
.size(); i
++)
1130 ScBaseCell
* pCell
= maItems
[i
].pCell
;
1131 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
1133 ScFormulaCell
* pFCell
= static_cast<ScFormulaCell
*>(pCell
);
1134 if (pFCell
->NeedsListening())
1136 SCROW nRow
= maItems
[i
].nRow
;
1137 pFCell
->StartListeningTo( pDocument
);
1138 if ( nRow
!= maItems
[i
].nRow
)
1139 Search( nRow
, i
); // Insert Listener?
1146 void ScColumn::BroadcastInArea( SCROW nRow1
, SCROW nRow2
)
1148 if (maItems
.empty())
1153 Search(nRow1
, nIndex
);
1155 while ( nIndex
< maItems
.size() && (nRow
= maItems
[nIndex
].nRow
) <= nRow2
)
1157 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1158 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
1159 ((ScFormulaCell
*)pCell
)->SetDirty();
1161 pDocument
->Broadcast( ScHint(SC_HINT_DATACHANGED
,
1162 ScAddress(nCol
, nRow
, nTab
)));
1168 void ScColumn::StartListeningInArea( sc::StartListeningContext
& rCxt
, SCROW nRow1
, SCROW nRow2
)
1170 if (maItems
.empty())
1175 Search( nRow1
, nIndex
);
1176 while ( nIndex
< maItems
.size() && (nRow
= maItems
[nIndex
].nRow
) <= nRow2
)
1178 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1179 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
1180 ((ScFormulaCell
*)pCell
)->StartListeningTo(rCxt
);
1181 if ( nRow
!= maItems
[nIndex
].nRow
)
1182 Search( nRow
, nIndex
); // Inserted via Listening
1190 void applyTextNumFormat( ScColumn
& rCol
, ScDocument
& rDoc
, SCROW nRow
, SvNumberFormatter
* pFormatter
)
1192 sal_uInt32 nFormat
= pFormatter
->GetStandardFormat(NUMBERFORMAT_TEXT
);
1193 ScPatternAttr
aNewAttrs(rDoc
.GetPool());
1194 SfxItemSet
& rSet
= aNewAttrs
.GetItemSet();
1195 rSet
.Put(SfxUInt32Item(ATTR_VALUE_FORMAT
, nFormat
));
1196 rCol
.ApplyPattern(nRow
, aNewAttrs
);
1201 bool ScColumn::ParseString(
1202 ScCellValue
& rCell
, SCROW nRow
, SCTAB nTabP
, const String
& rString
,
1203 formula::FormulaGrammar::AddressConvention eConv
,
1204 ScSetStringParam
* pParam
)
1209 bool bNumFmtSet
= false;
1211 ScSetStringParam aParam
;
1216 sal_uInt32 nIndex
= 0;
1217 sal_uInt32 nOldIndex
= 0;
1218 sal_Unicode cFirstChar
;
1219 if (!aParam
.mpNumFormatter
)
1220 aParam
.mpNumFormatter
= pDocument
->GetFormatTable();
1222 nIndex
= nOldIndex
= GetNumberFormat( nRow
);
1223 if ( rString
.Len() > 1
1224 && aParam
.mpNumFormatter
->GetType(nIndex
) != NUMBERFORMAT_TEXT
)
1225 cFirstChar
= rString
.GetChar(0);
1227 cFirstChar
= 0; // Text
1229 if ( cFirstChar
== '=' )
1231 if ( rString
.Len() == 1 ) // = Text
1233 else if (aParam
.meSetTextNumFormat
== ScSetStringParam::Always
)
1235 // Set the cell format type to Text.
1236 applyTextNumFormat(*this, *pDocument
, nRow
, aParam
.mpNumFormatter
);
1242 pDocument
, ScAddress(nCol
, nRow
, nTabP
), rString
,
1243 formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_DEFAULT
, eConv
),
1246 else if ( cFirstChar
== '\'') // 'Text
1248 bool bNumeric
= false;
1249 if (aParam
.mbHandleApostrophe
)
1251 // Cell format is not 'Text', and the first char
1252 // is an apostrophe. Check if the input is considered a number.
1253 String aTest
= rString
.Copy(1);
1255 bNumeric
= aParam
.mpNumFormatter
->IsNumberFormat(aTest
, nIndex
, fTest
);
1257 // This is a number. Strip out the first char.
1261 // This is normal text. Take it as-is.
1270 if (aParam
.mbDetectNumberFormat
)
1272 if (!aParam
.mpNumFormatter
->IsNumberFormat(rString
, nIndex
, nVal
))
1275 if ( aParam
.mpNumFormatter
)
1277 // convert back to the original language if a built-in format was detected
1278 const SvNumberformat
* pOldFormat
= aParam
.mpNumFormatter
->GetEntry( nOldIndex
);
1280 nIndex
= aParam
.mpNumFormatter
->GetFormatForLanguageIfBuiltIn( nIndex
, pOldFormat
->GetLanguage() );
1284 if ( nIndex
!= nOldIndex
)
1286 // #i22345# New behavior: Apply the detected number format only if
1287 // the old one was the default number, date, time or boolean format.
1288 // Exception: If the new format is boolean, always apply it.
1290 bool bOverwrite
= false;
1291 const SvNumberformat
* pOldFormat
= aParam
.mpNumFormatter
->GetEntry( nOldIndex
);
1294 short nOldType
= pOldFormat
->GetType() & ~NUMBERFORMAT_DEFINED
;
1295 if ( nOldType
== NUMBERFORMAT_NUMBER
|| nOldType
== NUMBERFORMAT_DATE
||
1296 nOldType
== NUMBERFORMAT_TIME
|| nOldType
== NUMBERFORMAT_LOGICAL
)
1298 if ( nOldIndex
== aParam
.mpNumFormatter
->GetStandardFormat(
1299 nOldType
, pOldFormat
->GetLanguage() ) )
1301 bOverwrite
= true; // default of these types can be overwritten
1305 if ( !bOverwrite
&& aParam
.mpNumFormatter
->GetType( nIndex
) == NUMBERFORMAT_LOGICAL
)
1307 bOverwrite
= true; // overwrite anything if boolean was detected
1312 ApplyAttr( nRow
, SfxUInt32Item( ATTR_VALUE_FORMAT
,
1313 (sal_uInt32
) nIndex
) );
1318 else if (aParam
.meSetTextNumFormat
!= ScSetStringParam::Always
)
1320 // Only check if the string is a regular number.
1321 const LocaleDataWrapper
* pLocale
= aParam
.mpNumFormatter
->GetLocaleData();
1325 LocaleDataItem aLocaleItem
= pLocale
->getLocaleItem();
1326 const OUString
& rDecSep
= aLocaleItem
.decimalSeparator
;
1327 const OUString
& rGroupSep
= aLocaleItem
.thousandSeparator
;
1328 if (rDecSep
.getLength() != 1 || rGroupSep
.getLength() != 1)
1331 sal_Unicode dsep
= rDecSep
.getStr()[0];
1332 sal_Unicode gsep
= rGroupSep
.getStr()[0];
1334 if (!ScStringUtil::parseSimpleNumber(rString
, dsep
, gsep
, nVal
))
1342 if (rCell
.meType
== CELLTYPE_NONE
)
1344 if (aParam
.meSetTextNumFormat
!= ScSetStringParam::Never
&& aParam
.mpNumFormatter
->IsNumberFormat(rString
, nIndex
, nVal
))
1346 // Set the cell format type to Text.
1347 applyTextNumFormat(*this, *pDocument
, nRow
, aParam
.mpNumFormatter
);
1358 * Returns true if the cell format was set as well
1360 bool ScColumn::SetString( SCROW nRow
, SCTAB nTabP
, const String
& rString
,
1361 formula::FormulaGrammar::AddressConvention eConv
,
1362 ScSetStringParam
* pParam
)
1364 if (!ValidRow(nRow
))
1367 ScCellValue aNewCell
;
1368 bool bNumFmtSet
= ParseString(aNewCell
, nRow
, nTabP
, rString
, eConv
, pParam
);
1369 aNewCell
.release(*this, nRow
);
1371 // Do not set Formats and Formulas here anymore!
1372 // These are queried during output
1377 void ScColumn::SetEditText( SCROW nRow
, EditTextObject
* pEditText
)
1379 Insert(nRow
, new ScEditCell(pEditText
, pDocument
));
1382 void ScColumn::SetEditText( SCROW nRow
, const EditTextObject
& rEditText
, const SfxItemPool
* pEditPool
)
1384 Insert(nRow
, new ScEditCell(rEditText
, pDocument
, pEditPool
));
1387 void ScColumn::SetFormula( SCROW nRow
, const ScTokenArray
& rArray
, formula::FormulaGrammar::Grammar eGram
)
1389 ScAddress
aPos(nCol
, nRow
, nTab
);
1390 ScFormulaCell
* pCell
= new ScFormulaCell(pDocument
, aPos
, &rArray
, eGram
);
1391 sal_uInt32 nCellFormat
= GetNumberFormat( nRow
);
1392 if( (nCellFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) != 0)
1393 pCell
->SetNeedNumberFormat(true);
1394 Insert(nRow
, pCell
);
1397 void ScColumn::SetFormula( SCROW nRow
, const OUString
& rFormula
, formula::FormulaGrammar::Grammar eGram
)
1399 ScAddress
aPos(nCol
, nRow
, nTab
);
1400 ScFormulaCell
* pCell
= new ScFormulaCell(pDocument
, aPos
, rFormula
, eGram
);
1402 sal_uInt32 nCellFormat
= GetNumberFormat( nRow
);
1403 if( (nCellFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) != 0)
1404 pCell
->SetNeedNumberFormat(true);
1405 Insert(nRow
, pCell
);
1408 void ScColumn::SetFormulaCell( SCROW nRow
, ScFormulaCell
* pCell
)
1410 Insert(nRow
, pCell
);
1413 void ScColumn::GetFilterEntries(SCROW nStartRow
, SCROW nEndRow
, std::vector
<ScTypedStrData
>& rStrings
, bool& rHasDates
)
1415 bool bHasDates
= false;
1416 SvNumberFormatter
* pFormatter
= pDocument
->GetFormatTable();
1420 Search( nStartRow
, nIndex
);
1422 for (; nIndex
< maItems
.size(); ++nIndex
)
1424 SCROW nRow
= maItems
[nIndex
].nRow
;
1428 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1429 ScRefCellValue aCell
;
1430 aCell
.assign(*maItems
[nIndex
].pCell
);
1431 sal_uLong nFormat
= GetNumberFormat( nRow
);
1433 ScCellFormat::GetInputString(aCell
, nFormat
, aString
, *pFormatter
, pDocument
);
1435 if ( pDocument
->HasStringData( nCol
, nRow
, nTab
) )
1437 rStrings
.push_back(ScTypedStrData(aString
));
1441 double nValue
= 0.0;
1443 switch ( pCell
->GetCellType() )
1445 case CELLTYPE_VALUE
:
1446 nValue
= ((ScValueCell
*)pCell
)->GetValue();
1449 case CELLTYPE_FORMULA
:
1451 ScFormulaCell
* pFC
= static_cast<ScFormulaCell
*>(pCell
);
1452 sal_uInt16 nErr
= pFC
->GetErrCode();
1455 // Error cell is evaluated as string (for now).
1456 String aErr
= ScGlobal::GetErrorString(nErr
);
1459 rStrings
.push_back(ScTypedStrData(aErr
));
1464 nValue
= pFC
->GetValue();
1473 short nType
= pFormatter
->GetType(nFormat
);
1474 if ((nType
& NUMBERFORMAT_DATE
) && !(nType
& NUMBERFORMAT_TIME
))
1476 // special case for date values. Disregard the time
1477 // element if the number format is of date type.
1478 nValue
= ::rtl::math::approxFloor(nValue
);
1483 rStrings
.push_back(ScTypedStrData(aString
, nValue
, ScTypedStrData::Value
));
1486 rHasDates
= bHasDates
;
1490 // GetDataEntries - Strings from continuous Section around nRow
1493 // DATENT_MAX - max. number of entries in list for auto entry
1494 // DATENT_SEARCH - max. number of cells that get transparent - new: only count Strings
1495 #define DATENT_MAX 200
1496 #define DATENT_SEARCH 2000
1499 bool ScColumn::GetDataEntries(SCROW nStartRow
, std::set
<ScTypedStrData
>& rStrings
, bool bLimit
)
1501 sal_Bool bFound
= false;
1503 sal_Bool bThisUsed
= Search( nStartRow
, nThisIndex
);
1505 sal_uInt16 nCells
= 0;
1507 // The limitation to neighbouring cells (without gaps) is not wanted anymore
1508 // (Featurecommission for 5.1), search upwards/downwards instead so that
1509 // nearby cell are cought at least first.
1510 // TODO: Compare distances of cell numbers? Performance??
1512 SCSIZE nUpIndex
= nThisIndex
; // Points after the row
1513 SCSIZE nDownIndex
= nThisIndex
; // Points to the row
1515 ++nDownIndex
; // Skip starting row
1517 while ( nUpIndex
|| nDownIndex
< maItems
.size() )
1519 if ( nUpIndex
) // Up
1521 ScBaseCell
* pCell
= maItems
[nUpIndex
-1].pCell
;
1522 CellType eType
= pCell
->GetCellType();
1523 if (eType
== CELLTYPE_STRING
|| eType
== CELLTYPE_EDIT
) // Only Strings are of interest
1525 if (eType
== CELLTYPE_STRING
)
1526 aString
= ((ScStringCell
*)pCell
)->GetString();
1528 aString
= ((ScEditCell
*)pCell
)->GetString();
1530 bool bInserted
= rStrings
.insert(ScTypedStrData(aString
)).second
;
1531 if (bInserted
&& bLimit
&& rStrings
.size() >= DATENT_MAX
)
1532 break; // Maximum reached
1536 if (++nCells
>= DATENT_SEARCH
)
1537 break; // Searched enough
1542 if ( nDownIndex
< maItems
.size() ) // Down
1544 ScBaseCell
* pCell
= maItems
[nDownIndex
].pCell
;
1545 CellType eType
= pCell
->GetCellType();
1546 if (eType
== CELLTYPE_STRING
|| eType
== CELLTYPE_EDIT
) // Only Strings are of interest
1548 if (eType
== CELLTYPE_STRING
)
1549 aString
= ((ScStringCell
*)pCell
)->GetString();
1551 aString
= ((ScEditCell
*)pCell
)->GetString();
1553 bool bInserted
= rStrings
.insert(ScTypedStrData(aString
)).second
;
1554 if (bInserted
&& bLimit
&& rStrings
.size() >= DATENT_MAX
)
1555 break; // Maximum reached
1559 if (++nCells
>= DATENT_SEARCH
)
1560 break; // Searched enough
1570 #undef DATENT_SEARCH
1573 void ScColumn::RemoveProtected( SCROW nStartRow
, SCROW nEndRow
)
1575 ScAttrIterator
aAttrIter( pAttrArray
, nStartRow
, nEndRow
);
1579 const ScPatternAttr
* pPattern
= aAttrIter
.Next( nTop
, nBottom
);
1582 const ScProtectionAttr
* pAttr
= (const ScProtectionAttr
*)&pPattern
->GetItem(ATTR_PROTECTION
);
1583 if ( pAttr
->GetHideCell() )
1584 DeleteArea( nTop
, nBottom
, IDF_CONTENTS
);
1585 else if ( pAttr
->GetHideFormula() )
1587 Search( nTop
, nIndex
);
1588 while ( nIndex
<maItems
.size() && maItems
[nIndex
].nRow
<=nBottom
)
1590 if ( maItems
[nIndex
].pCell
->GetCellType() == CELLTYPE_FORMULA
)
1592 ScFormulaCell
* pFormula
= (ScFormulaCell
*)maItems
[nIndex
].pCell
;
1593 if (pFormula
->IsValue())
1595 double nVal
= pFormula
->GetValue();
1596 maItems
[nIndex
].pCell
= new ScValueCell( nVal
);
1600 String aString
= pFormula
->GetString();
1601 maItems
[nIndex
].pCell
= new ScStringCell( aString
);
1605 SetTextWidth(maItems
[nIndex
].nRow
, TEXTWIDTH_DIRTY
);
1606 CellStorageModified();
1612 pPattern
= aAttrIter
.Next( nTop
, nBottom
);
1617 void ScColumn::SetError( SCROW nRow
, const sal_uInt16 nError
)
1621 ScFormulaCell
* pCell
= new ScFormulaCell
1622 ( pDocument
, ScAddress( nCol
, nRow
, nTab
) );
1623 pCell
->SetErrCode( nError
);
1624 Insert( nRow
, pCell
);
1628 void ScColumn::SetRawString( SCROW nRow
, const OUString
& rStr
)
1630 if (!ValidRow(nRow
))
1633 ScBaseCell
* pCell
= new ScStringCell(rStr
);
1634 Insert(nRow
, pCell
);
1637 void ScColumn::SetValue( SCROW nRow
, const double& rVal
)
1641 ScBaseCell
* pCell
= new ScValueCell(rVal
);
1642 Insert( nRow
, pCell
);
1647 void ScColumn::GetString( SCROW nRow
, OUString
& rString
) const
1651 if (Search(nRow
, nIndex
))
1653 ScRefCellValue aCell
;
1654 aCell
.assign(*maItems
[nIndex
].pCell
);
1656 // ugly hack for ordering problem with GetNumberFormat and missing inherited formats
1657 if(aCell
.meType
== CELLTYPE_FORMULA
)
1658 aCell
.mpFormula
->MaybeInterpret();
1660 sal_uLong nFormat
= GetNumberFormat( nRow
);
1661 ScCellFormat::GetString(aCell
, nFormat
, rString
, &pColor
, *(pDocument
->GetFormatTable()), pDocument
);
1664 rString
= EMPTY_OUSTRING
;
1667 const OUString
* ScColumn::GetStringCell( SCROW nRow
) const
1670 if (!Search(nRow
, nIndex
))
1673 const ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1674 if (pCell
->GetCellType() != CELLTYPE_STRING
)
1677 return static_cast<const ScStringCell
*>(pCell
)->GetStringPtr();
1680 double* ScColumn::GetValueCell( SCROW nRow
)
1683 if (!Search(nRow
, nIndex
))
1686 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1687 if (pCell
->GetCellType() != CELLTYPE_VALUE
)
1690 return static_cast<ScValueCell
*>(pCell
)->GetValuePtr();
1693 void ScColumn::GetInputString( SCROW nRow
, OUString
& rString
) const
1696 if (Search(nRow
, nIndex
))
1698 ScRefCellValue aCell
;
1699 aCell
.assign(*maItems
[nIndex
].pCell
);
1700 sal_uLong nFormat
= GetNumberFormat( nRow
);
1701 ScCellFormat::GetInputString(aCell
, nFormat
, rString
, *(pDocument
->GetFormatTable()), pDocument
);
1704 rString
= OUString();
1708 double ScColumn::GetValue( SCROW nRow
) const
1711 if (Search(nRow
, nIndex
))
1713 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1714 switch (pCell
->GetCellType())
1716 case CELLTYPE_VALUE
:
1717 return ((ScValueCell
*)pCell
)->GetValue();
1719 case CELLTYPE_FORMULA
:
1721 if (((ScFormulaCell
*)pCell
)->IsValue())
1722 return ((ScFormulaCell
*)pCell
)->GetValue();
1734 const EditTextObject
* ScColumn::GetEditText( SCROW nRow
) const
1737 if (!Search(nRow
, nIndex
))
1740 const ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1741 if (pCell
->GetCellType() != CELLTYPE_EDIT
)
1744 const ScEditCell
* pEditCell
= static_cast<const ScEditCell
*>(pCell
);
1745 return pEditCell
->GetData();
1748 void ScColumn::RemoveEditTextCharAttribs( SCROW nRow
, const ScPatternAttr
& rAttr
)
1751 if (!Search(nRow
, nIndex
))
1754 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1755 if (pCell
->GetCellType() != CELLTYPE_EDIT
)
1758 ScEditCell
* pEditCell
= static_cast<ScEditCell
*>(pCell
);
1759 pEditCell
->RemoveCharAttribs(rAttr
);
1762 void ScColumn::GetFormula( SCROW nRow
, OUString
& rFormula
) const
1765 if (Search(nRow
, nIndex
))
1767 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1768 if (pCell
->GetCellType() == CELLTYPE_FORMULA
)
1769 ((ScFormulaCell
*)pCell
)->GetFormula( rFormula
);
1771 rFormula
= OUString();
1774 rFormula
= OUString();
1777 const ScTokenArray
* ScColumn::GetFormulaTokens( SCROW nRow
) const
1779 const ScFormulaCell
* pCell
= FetchFormulaCell(nRow
);
1783 return pCell
->GetCode();
1786 const ScFormulaCell
* ScColumn::GetFormulaCell( SCROW nRow
) const
1788 return FetchFormulaCell(nRow
);
1791 ScFormulaCell
* ScColumn::GetFormulaCell( SCROW nRow
)
1793 return const_cast<ScFormulaCell
*>(FetchFormulaCell(nRow
));
1796 CellType
ScColumn::GetCellType( SCROW nRow
) const
1799 if (Search(nRow
, nIndex
))
1800 return maItems
[nIndex
].pCell
->GetCellType();
1801 return CELLTYPE_NONE
;
1804 SCSIZE
ScColumn::GetCellCount() const
1806 return maItems
.size();
1809 sal_uInt16
ScColumn::GetErrCode( SCROW nRow
) const
1812 if (Search(nRow
, nIndex
))
1814 ScBaseCell
* pCell
= maItems
[nIndex
].pCell
;
1815 if (pCell
->GetCellType() == CELLTYPE_FORMULA
)
1816 return ((ScFormulaCell
*)pCell
)->GetErrCode();
1822 bool ScColumn::HasStringData( SCROW nRow
) const
1825 if (Search(nRow
, nIndex
))
1826 return (maItems
[nIndex
].pCell
)->HasStringData();
1831 bool ScColumn::HasValueData( SCROW nRow
) const
1834 if (Search(nRow
, nIndex
))
1835 return (maItems
[nIndex
].pCell
)->HasValueData();
1840 * Return true if there is a string or editcell in the range
1842 bool ScColumn::HasStringCells( SCROW nStartRow
, SCROW nEndRow
) const
1844 if ( !maItems
.empty() )
1847 Search( nStartRow
, nIndex
);
1848 while ( nIndex
< maItems
.size() && maItems
[nIndex
].nRow
<= nEndRow
)
1850 CellType eType
= maItems
[nIndex
].pCell
->GetCellType();
1851 if ( eType
== CELLTYPE_STRING
|| eType
== CELLTYPE_EDIT
)
1860 sal_Int32
ScColumn::GetMaxStringLen( SCROW nRowStart
, SCROW nRowEnd
, CharSet eCharSet
) const
1862 sal_Int32 nStringLen
= 0;
1863 if ( !maItems
.empty() )
1867 bool bIsOctetTextEncoding
= rtl_isOctetTextEncoding( eCharSet
);
1868 SvNumberFormatter
* pNumFmt
= pDocument
->GetFormatTable();
1871 Search( nRowStart
, nIndex
);
1872 while ( nIndex
< maItems
.size() && (nRow
= maItems
[nIndex
].nRow
) <= nRowEnd
)
1874 ScRefCellValue aCell
;
1875 aCell
.assign(*maItems
[nIndex
].pCell
);
1877 sal_uLong nFormat
= (sal_uLong
) ((SfxUInt32Item
*) GetAttr(
1878 nRow
, ATTR_VALUE_FORMAT
))->GetValue();
1879 ScCellFormat::GetString(aCell
, nFormat
, aString
, &pColor
, *pNumFmt
, pDocument
);
1881 if (bIsOctetTextEncoding
)
1883 if (!aString
.convertToString( &aOString
, eCharSet
,
1884 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
1885 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
))
1887 // TODO: anything? this is used by the dBase export filter
1888 // that throws an error anyway, but in case of another
1889 // context we might want to indicate a conversion error
1892 nLen
= aOString
.getLength();
1895 nLen
= aString
.getLength() * sizeof(sal_Unicode
);
1896 if ( nStringLen
< nLen
)
1905 xub_StrLen
ScColumn::GetMaxNumberStringLen(
1906 sal_uInt16
& nPrecision
, SCROW nRowStart
, SCROW nRowEnd
) const
1908 xub_StrLen nStringLen
= 0;
1911 if ( !maItems
.empty() )
1915 SvNumberFormatter
* pNumFmt
= pDocument
->GetFormatTable();
1916 sal_uInt16 nMaxGeneralPrecision
= pDocument
->GetDocOptions().GetStdPrecision();
1917 // Limit the decimals passed to doubleToUString().
1918 // Also, the dBaseIII maximum precision is 15.
1919 if (nMaxGeneralPrecision
> 15)
1920 nMaxGeneralPrecision
= 15;
1921 bool bHaveSigned
= false;
1924 Search( nRowStart
, nIndex
);
1925 while ( nIndex
< maItems
.size() && (nRow
= maItems
[nIndex
].nRow
) <= nRowEnd
)
1927 ScRefCellValue aCell
;
1928 aCell
.assign(*maItems
[nIndex
].pCell
);
1929 CellType eType
= aCell
.meType
;
1930 if ( eType
== CELLTYPE_VALUE
|| (eType
== CELLTYPE_FORMULA
1931 && aCell
.mpFormula
->IsValue()) )
1935 sal_uInt16 nCellPrecision
= nMaxGeneralPrecision
;
1936 if (eType
== CELLTYPE_FORMULA
)
1938 // Limit unformatted formula cell precision to precision
1939 // encountered so far, if any, otherwise we'd end up with 15 just
1940 // because of =1/3 ... If no precision yet then arbitrarily limit
1941 // to a maximum of 4 unless a maximum general precision is set.
1943 nCellPrecision
= nPrecision
;
1945 nCellPrecision
= (nMaxGeneralPrecision
>= 15) ? 4 : nMaxGeneralPrecision
;
1948 double fVal
= aCell
.getValue();
1949 if (!bHaveSigned
&& fVal
< 0.0)
1953 sal_uLong nFormat
= (sal_uLong
) ((SfxUInt32Item
*) GetAttr(
1954 nRow
, ATTR_VALUE_FORMAT
))->GetValue();
1955 if (nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
)
1957 aSep
= pNumFmt
->GetFormatDecimalSep(nFormat
);
1958 ScCellFormat::GetInputString(aCell
, nFormat
, aString
, *pNumFmt
, pDocument
);
1959 const SvNumberformat
* pEntry
= pNumFmt
->GetEntry( nFormat
);
1962 bool bThousand
, bNegRed
;
1963 sal_uInt16 nLeading
;
1964 pEntry
->GetFormatSpecialInfo(bThousand
, bNegRed
, nPrec
, nLeading
);
1967 nPrec
= pNumFmt
->GetFormatPrecision( nFormat
);
1971 if (nPrecision
>= nMaxGeneralPrecision
)
1972 break; // early bail out for nothing changes here
1976 // 0 doesn't change precision, but set a maximum length if none yet.
1982 // Simple number string with at most 15 decimals and trailing
1983 // decimal zeros eliminated.
1985 aString
= rtl::math::doubleToUString( fVal
, rtl_math_StringFormat_F
, nCellPrecision
, '.', true);
1986 nPrec
= SvNumberFormatter::UNLIMITED_PRECISION
;
1989 sal_Int32 nLen
= aString
.getLength();
1991 // Ignore empty string.
1994 if (nPrec
== SvNumberFormatter::UNLIMITED_PRECISION
&& nPrecision
< nMaxGeneralPrecision
)
1996 if (nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
)
1998 // For some reason we couldn't obtain a precision from the
1999 // format, retry with simple number string.
2001 aString
= rtl::math::doubleToUString( fVal
, rtl_math_StringFormat_F
, nCellPrecision
, '.', true);
2002 nLen
= aString
.getLength();
2004 sal_Int32 nSep
= aString
.indexOf( aSep
);
2006 nPrec
= aString
.getLength() - nSep
- 1;
2010 if (nPrec
!= SvNumberFormatter::UNLIMITED_PRECISION
&& nPrec
> nPrecision
)
2014 { // less than nPrecision in string => widen it
2015 // more => shorten it
2016 sal_Int32 nTmp
= aString
.indexOf( aSep
);
2018 nLen
+= nPrecision
+ aSep
.Len();
2021 nTmp
= aString
.getLength() - (nTmp
+ aSep
.Len());
2022 if ( nTmp
!= nPrecision
)
2023 nLen
+= nPrecision
- nTmp
;
2024 // nPrecision > nTmp : nLen + Diff
2025 // nPrecision < nTmp : nLen - Diff
2029 // Enlarge for sign if necessary. Bear in mind that
2030 // GetMaxNumberStringLen() is for determining dBase decimal field width
2031 // and precision where the overall field width must include the sign.
2032 // Fitting -1 into "#.##" (width 4, 2 decimals) does not work.
2033 if (bHaveSigned
&& fVal
>= 0.0)
2036 if ( nStringLen
< nLen
)
2049 struct CellGroupSetter
: std::unary_function
<ColEntry
, void>
2051 ScFormulaCellGroupRef mxGroup
;
2053 CellGroupSetter(const ScFormulaCellGroupRef
& xGroup
) : mxGroup(xGroup
) {}
2055 void operator() (ColEntry
& rEntry
)
2057 if (rEntry
.pCell
&& rEntry
.pCell
->GetCellType() == CELLTYPE_FORMULA
)
2058 static_cast<ScFormulaCell
*>(rEntry
.pCell
)->SetCellGroup(mxGroup
);
2064 // Very[!] slow way to look for and merge contiguous runs
2065 // of similar formulae into a formulagroup
2066 void ScColumn::RebuildFormulaGroups()
2068 if ( maItems
.empty() || !mbDirtyGroups
)
2071 // clear double groups
2072 std::for_each(maDoubles
.begin(), maDoubles
.end(), ScDeleteObjectByPtr
<ColDoubleEntry
>());
2075 // clear previous groups
2076 ScFormulaCellGroupRef xNone
;
2077 std::for_each(maItems
.begin(), maItems
.end(), CellGroupSetter(xNone
));
2081 ColDoubleEntry
*pLastDouble
= NULL
;
2082 for (size_t i
= 1; i
< maItems
.size(); i
++)
2084 ColEntry
&rCur
= maItems
[ i
];
2085 ColEntry
&rPrev
= maItems
[ i
- 1 ];
2086 if ( ( rPrev
.nRow
!= rCur
.nRow
- 1 ) || // not contiguous
2087 !rCur
.pCell
|| !rPrev
.pCell
|| // paranoia
2088 rCur
.pCell
->GetCellType() != rPrev
.pCell
->GetCellType() ) // same type
2090 // Non-contiguous cell detected. Break the series.
2096 if ( rCur
.pCell
->GetCellType() == CELLTYPE_VALUE
)
2100 pLastDouble
= new ColDoubleEntry();
2101 pLastDouble
->mnStart
= rPrev
.nRow
;
2102 pLastDouble
->maData
.push_back(
2103 static_cast< ScValueCell
* >( rPrev
.pCell
)->GetValue() );
2104 maDoubles
.push_back( pLastDouble
);
2106 pLastDouble
->maData
.push_back(
2107 static_cast< ScValueCell
* >( rCur
.pCell
)->GetValue() );
2111 if ( rCur
.pCell
->GetCellType() != CELLTYPE_FORMULA
)
2114 // see if these formula tokens are identical.
2115 ScFormulaCell
*pCur
= static_cast< ScFormulaCell
*>( rCur
.pCell
);
2116 ScFormulaCell
*pPrev
= static_cast< ScFormulaCell
*>( rPrev
.pCell
);
2118 ScFormulaCell::CompareState eCompState
= pPrev
->CompareByTokenArray(pCur
);
2119 if (eCompState
== ScFormulaCell::NotEqual
)
2121 // different formula tokens.
2122 pCur
->SetCellGroup( xNone
);
2126 ScFormulaCellGroupRef xGroup
= pPrev
->GetCellGroup();
2129 // create a new group ...
2130 xGroup
.reset(new ScFormulaCellGroup
);
2131 xGroup
->mnStart
= rPrev
.nRow
;
2132 xGroup
->mbInvariant
= eCompState
== ScFormulaCell::EqualInvariant
;
2133 xGroup
->mnLength
= 2;
2135 maFnGroups
.push_back( xGroup
);
2137 pCur
->SetCellGroup( xGroup
);
2138 pPrev
->SetCellGroup( xGroup
);
2142 // existing group. extend its length.
2143 pCur
->SetCellGroup( xGroup
);
2148 #if OSL_DEBUG_LEVEL > 0
2149 if ( maDoubles
.size() + maFnGroups
.size() > 0 )
2152 fprintf( stderr
, "column %2d has %2d double span(s): ", (int)nCol
, (int)maDoubles
.size() );
2153 for (std::vector
< ColDoubleEntry
*>::iterator it
= maDoubles
.begin();
2154 it
!= maDoubles
.end(); ++it
)
2156 ScRange
aDoubleRange( nCol
, (*it
)->mnStart
, nTab
,
2157 nCol
, (*it
)->mnStart
+ (*it
)->maData
.size() - 1, nTab
);
2158 aDoubleRange
.Format( aStr
, SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
, pDocument
);
2159 fprintf( stderr
, "%s, ", OUStringToOString( aStr
, RTL_TEXTENCODING_UTF8
).getStr() );
2161 fprintf( stderr
, "\n" );
2163 fprintf( stderr
, "column %2d has %2d formula span(s): ", (int)nCol
, (int)maFnGroups
.size() );
2164 for (std::vector
< ScFormulaCellGroupRef
>::iterator it
= maFnGroups
.begin();
2165 it
!= maFnGroups
.end(); ++it
)
2167 ScRange
aDoubleRange( nCol
, (*it
)->mnStart
, nTab
,
2168 nCol
, (*it
)->mnStart
+ (*it
)->mnLength
- 1, nTab
);
2169 aDoubleRange
.Format( aStr
, SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
, pDocument
);
2170 fprintf( stderr
, "%s, ", OUStringToOString( aStr
, RTL_TEXTENCODING_UTF8
).getStr() );
2172 fprintf( stderr
, "\n" );
2176 mbDirtyGroups
= false;
2179 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */