Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <algorithm>
21 : #include <deque>
22 :
23 : #include <boost/bind.hpp>
24 : #include <sal/macros.h>
25 : #include <vcl/mapmod.hxx>
26 : #include <editeng/editobj.hxx>
27 : #include <editeng/editstat.hxx>
28 : #include "editeng/fieldupdater.hxx"
29 :
30 : #include "cell.hxx"
31 : #include "compiler.hxx"
32 : #include "formula/errorcodes.hxx"
33 : #include "document.hxx"
34 : #include "rangenam.hxx"
35 : #include "rechead.hxx"
36 : #include "refupdat.hxx"
37 : #include "scmatrix.hxx"
38 : #include "editutil.hxx"
39 : #include "chgtrack.hxx"
40 : #include "externalrefmgr.hxx"
41 : #include "scitems.hxx"
42 : #include "patattr.hxx"
43 : #include <rtl/strbuf.hxx>
44 :
45 : using namespace formula;
46 :
47 : // STATIC DATA -----------------------------------------------------------
48 :
49 : #ifdef USE_MEMPOOL
50 5 : IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell )
51 : #endif
52 :
53 : // ============================================================================
54 :
55 55 : ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP,
56 : const SfxItemPool* pFromPool ) :
57 : ScBaseCell( CELLTYPE_EDIT ),
58 : pString( NULL ),
59 55 : pDoc( pDocP )
60 : {
61 55 : SetTextObject( pObject, pFromPool );
62 55 : }
63 :
64 0 : ScEditCell::ScEditCell(const ScEditCell& rCell, ScDocument& rDoc, const ScAddress& rDestPos) :
65 0 : ScBaseCell(rCell), pString(NULL), pDoc(&rDoc)
66 : {
67 0 : SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() );
68 0 : UpdateFields(rDestPos.Tab());
69 0 : }
70 :
71 29 : ScEditCell::ScEditCell( const rtl::OUString& rString, ScDocument* pDocP ) :
72 : ScBaseCell( CELLTYPE_EDIT ),
73 : pString( NULL ),
74 29 : pDoc( pDocP )
75 : {
76 : OSL_ENSURE( rString.indexOf('\n') != -1 ||
77 : rString.indexOf(CHAR_CR) != -1,
78 : "EditCell mit einfachem Text !?!?" );
79 :
80 29 : EditEngine& rEngine = pDoc->GetEditEngine();
81 29 : rEngine.SetText( rString );
82 29 : pData = rEngine.CreateTextObject();
83 29 : }
84 :
85 168 : ScEditCell::~ScEditCell()
86 : {
87 84 : delete pData;
88 84 : delete pString;
89 :
90 : #if OSL_DEBUG_LEVEL > 0
91 : eCellType = CELLTYPE_DESTROYED;
92 : #endif
93 84 : }
94 :
95 0 : void ScEditCell::SetData( const EditTextObject* pObject,
96 : const SfxItemPool* pFromPool )
97 : {
98 0 : if ( pString )
99 : {
100 0 : delete pString;
101 0 : pString = NULL;
102 : }
103 0 : delete pData;
104 0 : SetTextObject( pObject, pFromPool );
105 0 : }
106 :
107 17 : void ScEditCell::GetData( const EditTextObject*& rpObject ) const
108 : {
109 17 : rpObject = pData;
110 17 : }
111 :
112 51 : rtl::OUString ScEditCell::GetString() const
113 : {
114 51 : if ( pString )
115 32 : return *pString;
116 :
117 19 : if ( pData )
118 : {
119 : // Also Text from URL fields, Doc-Engine is a ScFieldEditEngine
120 19 : EditEngine& rEngine = pDoc->GetEditEngine();
121 19 : rEngine.SetText( *pData );
122 19 : rtl::OUString sRet = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs
123 : // cache short strings for formulas
124 19 : if ( sRet.getLength() < 256 )
125 19 : pString = new rtl::OUString(sRet); //! non-const
126 19 : return sRet;
127 : }
128 :
129 0 : return rtl::OUString();
130 : }
131 :
132 0 : void ScEditCell::RemoveCharAttribs( const ScPatternAttr& rAttr )
133 : {
134 : const struct {
135 : sal_uInt16 nAttrType;
136 : sal_uInt16 nCharType;
137 : } AttrTypeMap[] = {
138 : { ATTR_FONT, EE_CHAR_FONTINFO },
139 : { ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT },
140 : { ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT },
141 : { ATTR_FONT_COLOR, EE_CHAR_COLOR }
142 0 : };
143 0 : sal_uInt16 nMapCount = sizeof (AttrTypeMap) / sizeof (AttrTypeMap[0]);
144 :
145 0 : const SfxItemSet& rSet = rAttr.GetItemSet();
146 : const SfxPoolItem* pItem;
147 0 : for (sal_uInt16 i = 0; i < nMapCount; ++i)
148 : {
149 0 : if ( rSet.GetItemState(AttrTypeMap[i].nAttrType, false, &pItem) == SFX_ITEM_SET )
150 0 : pData->RemoveCharAttribs(AttrTypeMap[i].nCharType);
151 : }
152 0 : }
153 :
154 0 : void ScEditCell::UpdateFields(SCTAB nTab)
155 : {
156 0 : editeng::FieldUpdater aUpdater = pData->GetFieldUpdater();
157 0 : aUpdater.updateTableFields(nTab);
158 0 : }
159 :
160 55 : void ScEditCell::SetTextObject( const EditTextObject* pObject,
161 : const SfxItemPool* pFromPool )
162 : {
163 55 : if ( pObject )
164 : {
165 55 : if ( pFromPool && pDoc->GetEditPool() == pFromPool )
166 55 : pData = pObject->Clone();
167 : else
168 : { //! another "spool"
169 : // Sadly there is no other way to change the Pool than to
170 : // "spool" the Object through a corresponding Engine
171 0 : EditEngine& rEngine = pDoc->GetEditEngine();
172 0 : if ( pObject->HasOnlineSpellErrors() )
173 : {
174 0 : sal_uLong nControl = rEngine.GetControlWord();
175 0 : const sal_uLong nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS;
176 0 : bool bNewControl = ( (nControl & nSpellControl) != nSpellControl );
177 0 : if ( bNewControl )
178 0 : rEngine.SetControlWord( nControl | nSpellControl );
179 0 : rEngine.SetText( *pObject );
180 0 : pData = rEngine.CreateTextObject();
181 0 : if ( bNewControl )
182 0 : rEngine.SetControlWord( nControl );
183 : }
184 : else
185 : {
186 0 : rEngine.SetText( *pObject );
187 0 : pData = rEngine.CreateTextObject();
188 : }
189 : }
190 : }
191 : else
192 0 : pData = NULL;
193 55 : }
194 :
195 0 : ScEditDataArray::ScEditDataArray()
196 : {
197 0 : }
198 :
199 0 : ScEditDataArray::~ScEditDataArray()
200 : {
201 0 : }
202 :
203 0 : void ScEditDataArray::AddItem(SCTAB nTab, SCCOL nCol, SCROW nRow,
204 : EditTextObject* pOldData, EditTextObject* pNewData)
205 : {
206 0 : maArray.push_back(Item(nTab, nCol, nRow, pOldData, pNewData));
207 0 : }
208 :
209 0 : const ScEditDataArray::Item* ScEditDataArray::First()
210 : {
211 0 : maIter = maArray.begin();
212 0 : if (maIter == maArray.end())
213 0 : return NULL;
214 0 : return &(*maIter++);
215 : }
216 :
217 0 : const ScEditDataArray::Item* ScEditDataArray::Next()
218 : {
219 0 : if (maIter == maArray.end())
220 0 : return NULL;
221 0 : return &(*maIter++);
222 : }
223 :
224 : // ============================================================================
225 :
226 0 : ScEditDataArray::Item::Item(SCTAB nTab, SCCOL nCol, SCROW nRow,
227 : EditTextObject* pOldData, EditTextObject* pNewData) :
228 : mnTab(nTab),
229 : mnCol(nCol),
230 0 : mnRow(nRow)
231 : {
232 0 : mpOldData.reset(pOldData);
233 0 : mpNewData.reset(pNewData);
234 0 : }
235 :
236 0 : ScEditDataArray::Item::~Item()
237 : {
238 0 : }
239 :
240 0 : const EditTextObject* ScEditDataArray::Item::GetOldData() const
241 : {
242 0 : return mpOldData.get();
243 : }
244 :
245 0 : const EditTextObject* ScEditDataArray::Item::GetNewData() const
246 : {
247 0 : return mpNewData.get();
248 : }
249 :
250 0 : SCTAB ScEditDataArray::Item::GetTab() const
251 : {
252 0 : return mnTab;
253 : }
254 :
255 0 : SCCOL ScEditDataArray::Item::GetCol() const
256 : {
257 0 : return mnCol;
258 : }
259 :
260 0 : SCROW ScEditDataArray::Item::GetRow() const
261 : {
262 0 : return mnRow;
263 : }
264 :
265 : // ============================================================================
266 :
267 : namespace
268 : {
269 :
270 : using std::deque;
271 :
272 : typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&);
273 :
274 :
275 0 : static SCCOLROW lcl_GetCol(const ScSingleRefData& rData)
276 : {
277 0 : return rData.nCol;
278 : }
279 :
280 :
281 0 : static SCCOLROW lcl_GetRow(const ScSingleRefData& rData)
282 : {
283 0 : return rData.nRow;
284 : }
285 :
286 :
287 0 : static SCCOLROW lcl_GetTab(const ScSingleRefData& rData)
288 : {
289 0 : return rData.nTab;
290 : }
291 :
292 :
293 : /** Check if both references span the same range in selected dimension.
294 : */
295 : static bool
296 0 : lcl_checkRangeDimension(
297 : const SingleDoubleRefProvider& rRef1,
298 : const SingleDoubleRefProvider& rRef2,
299 : const DimensionSelector aWhich)
300 : {
301 : return
302 0 : aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1)
303 0 : && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2);
304 : }
305 :
306 :
307 : static bool
308 0 : lcl_checkRangeDimensions(
309 : const SingleDoubleRefProvider& rRef1,
310 : const SingleDoubleRefProvider& rRef2,
311 : bool& bCol, bool& bRow, bool& bTab)
312 : {
313 0 : const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol));
314 0 : const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow));
315 0 : const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab));
316 :
317 : // Test if exactly two dimensions are equal
318 0 : if (!(bSameCols ^ bSameRows ^ bSameTabs)
319 : && (bSameCols || bSameRows || bSameTabs))
320 : {
321 0 : bCol = !bSameCols;
322 0 : bRow = !bSameRows;
323 0 : bTab = !bSameTabs;
324 0 : return true;
325 : }
326 0 : return false;
327 : }
328 :
329 :
330 : /** Check if references in given reference list can possibly
331 : form a range. To do that, two of their dimensions must be the same.
332 : */
333 : static bool
334 0 : lcl_checkRangeDimensions(
335 : const deque<ScToken*>::const_iterator aBegin,
336 : const deque<ScToken*>::const_iterator aEnd,
337 : bool& bCol, bool& bRow, bool& bTab)
338 : {
339 0 : deque<ScToken*>::const_iterator aCur(aBegin);
340 0 : ++aCur;
341 0 : const SingleDoubleRefProvider aRef(**aBegin);
342 0 : bool bOk(false);
343 : {
344 0 : const SingleDoubleRefProvider aRefCur(**aCur);
345 0 : bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab);
346 : }
347 0 : while (bOk && aCur != aEnd)
348 : {
349 0 : const SingleDoubleRefProvider aRefCur(**aCur);
350 0 : bool bColTmp(false);
351 0 : bool bRowTmp(false);
352 0 : bool bTabTmp(false);
353 0 : bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
354 0 : bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
355 0 : ++aCur;
356 0 : }
357 :
358 0 : if (bOk && aCur == aEnd)
359 : {
360 0 : return true;
361 : }
362 0 : return false;
363 : }
364 :
365 :
366 : bool
367 0 : lcl_lessReferenceBy(
368 : const ScToken* const pRef1, const ScToken* const pRef2,
369 : const DimensionSelector aWhich)
370 : {
371 0 : const SingleDoubleRefProvider rRef1(*pRef1);
372 0 : const SingleDoubleRefProvider rRef2(*pRef2);
373 0 : return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1);
374 : }
375 :
376 :
377 : /** Returns true if range denoted by token pRef2 starts immediately after
378 : range denoted by token pRef1. Dimension, in which the comparison takes
379 : place, is given by aWhich.
380 : */
381 : bool
382 0 : lcl_isImmediatelyFollowing(
383 : const ScToken* const pRef1, const ScToken* const pRef2,
384 : const DimensionSelector aWhich)
385 : {
386 0 : const SingleDoubleRefProvider rRef1(*pRef1);
387 0 : const SingleDoubleRefProvider rRef2(*pRef2);
388 0 : return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1;
389 : }
390 :
391 :
392 : static bool
393 0 : lcl_checkIfAdjacent(
394 : const deque<ScToken*>& rReferences,
395 : const DimensionSelector aWhich)
396 : {
397 : typedef deque<ScToken*>::const_iterator Iter;
398 0 : Iter aBegin(rReferences.begin());
399 0 : Iter aEnd(rReferences.end());
400 0 : Iter aBegin1(aBegin);
401 0 : ++aBegin1, --aEnd;
402 : return std::equal(
403 : aBegin, aEnd, aBegin1,
404 0 : boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich));
405 : }
406 :
407 :
408 : static void
409 0 : lcl_fillRangeFromRefList(
410 : const deque<ScToken*>& rReferences, ScRange& rRange)
411 : {
412 : const ScSingleRefData aStart(
413 0 : SingleDoubleRefProvider(*rReferences.front()).Ref1);
414 0 : rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab);
415 : const ScSingleRefData aEnd(
416 0 : SingleDoubleRefProvider(*rReferences.back()).Ref2);
417 0 : rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab);
418 0 : }
419 :
420 :
421 : static bool
422 0 : lcl_refListFormsOneRange(
423 : const ScAddress& aPos, deque<ScToken*>& rReferences,
424 : ScRange& rRange)
425 : {
426 : std::for_each(
427 : rReferences.begin(), rReferences.end(),
428 : bind(&ScToken::CalcAbsIfRel, _1, aPos))
429 0 : ;
430 0 : if (rReferences.size() == 1) {
431 0 : lcl_fillRangeFromRefList(rReferences, rRange);
432 0 : return true;
433 : }
434 :
435 0 : bool bCell(false);
436 0 : bool bRow(false);
437 0 : bool bTab(false);
438 0 : if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(),
439 0 : bCell, bRow, bTab))
440 : {
441 : DimensionSelector aWhich;
442 0 : if (bCell)
443 : {
444 0 : aWhich = lcl_GetCol;
445 : }
446 0 : else if (bRow)
447 : {
448 0 : aWhich = lcl_GetRow;
449 : }
450 0 : else if (bTab)
451 : {
452 0 : aWhich = lcl_GetTab;
453 : }
454 : else
455 : {
456 : OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
457 0 : aWhich = lcl_GetRow; // initialize to avoid warning
458 : }
459 : // Sort the references by start of range
460 : std::sort(rReferences.begin(), rReferences.end(),
461 0 : boost::bind(lcl_lessReferenceBy, _1, _2, aWhich));
462 0 : if (lcl_checkIfAdjacent(rReferences, aWhich))
463 : {
464 0 : lcl_fillRangeFromRefList(rReferences, rRange);
465 0 : return true;
466 : }
467 : }
468 0 : return false;
469 : }
470 :
471 :
472 0 : bool lcl_isReference(const FormulaToken& rToken)
473 : {
474 : return
475 0 : rToken.GetType() == svSingleRef ||
476 0 : rToken.GetType() == svDoubleRef;
477 : }
478 :
479 : }
480 :
481 53 : bool ScFormulaCell::IsEmpty()
482 : {
483 53 : MaybeInterpret();
484 53 : return aResult.GetCellResultType() == formula::svEmptyCell;
485 : }
486 :
487 514 : bool ScFormulaCell::IsEmptyDisplayedAsString()
488 : {
489 514 : MaybeInterpret();
490 514 : return aResult.IsEmptyDisplayedAsString();
491 : }
492 :
493 3904 : bool ScFormulaCell::IsValue()
494 : {
495 3904 : MaybeInterpret();
496 3904 : return aResult.IsValue();
497 : }
498 :
499 3282 : double ScFormulaCell::GetValue()
500 : {
501 3282 : MaybeInterpret();
502 6564 : if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
503 3282 : !aResult.GetResultError())
504 3281 : return aResult.GetDouble();
505 1 : return 0.0;
506 : }
507 :
508 0 : double ScFormulaCell::GetValueAlways()
509 : {
510 : // for goal seek: return result value even if error code is set
511 0 : MaybeInterpret();
512 0 : return aResult.GetDouble();
513 : }
514 :
515 124 : rtl::OUString ScFormulaCell::GetString()
516 : {
517 124 : MaybeInterpret();
518 232 : if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
519 108 : !aResult.GetResultError())
520 108 : return aResult.GetString();
521 16 : return rtl::OUString();
522 : }
523 :
524 42 : const ScMatrix* ScFormulaCell::GetMatrix()
525 : {
526 42 : if ( pDocument->GetAutoCalc() )
527 : {
528 210 : if( IsDirtyOrInTableOpDirty()
529 : // Was stored !bDirty but an accompanying matrix cell was bDirty?
530 168 : || (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix()))
531 8 : Interpret();
532 : }
533 42 : return aResult.GetMatrix().get();
534 : }
535 :
536 0 : bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
537 : {
538 0 : switch ( cMatrixFlag )
539 : {
540 : case MM_FORMULA :
541 0 : rPos = aPos;
542 0 : return true;
543 : case MM_REFERENCE :
544 : {
545 0 : pCode->Reset();
546 0 : ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
547 0 : if( t )
548 : {
549 0 : ScSingleRefData& rRef = t->GetSingleRef();
550 0 : rRef.CalcAbsIfRel( aPos );
551 0 : if ( rRef.Valid() )
552 : {
553 0 : rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
554 0 : return true;
555 : }
556 : }
557 : }
558 0 : break;
559 : }
560 0 : return false;
561 : }
562 :
563 :
564 : /*
565 : Edge-Values:
566 :
567 : 8
568 : 4 16
569 : 2
570 :
571 : inside: 1
572 : outside: 0
573 : (reserved: open: 32)
574 : */
575 :
576 0 : sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos )
577 : {
578 0 : switch ( cMatrixFlag )
579 : {
580 : case MM_FORMULA :
581 : case MM_REFERENCE :
582 : {
583 : static SCCOL nC;
584 : static SCROW nR;
585 0 : ScAddress aOrg;
586 0 : if ( !GetMatrixOrigin( aOrg ) )
587 0 : return 0; // bad luck..
588 0 : if ( aOrg != rOrgPos )
589 : { // First time or a different matrix than last time.
590 0 : rOrgPos = aOrg;
591 : ScFormulaCell* pFCell;
592 0 : if ( cMatrixFlag == MM_REFERENCE )
593 0 : pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg );
594 : else
595 0 : pFCell = this; // this MM_FORMULA
596 : // There's only one this, don't compare pFCell==this.
597 0 : if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA
598 : && pFCell->cMatrixFlag == MM_FORMULA )
599 : {
600 0 : pFCell->GetMatColsRows( nC, nR );
601 0 : if ( nC == 0 || nR == 0 )
602 : {
603 : // No ScMatrixFormulaCellToken available yet, calculate new.
604 0 : nC = 1;
605 0 : nR = 1;
606 0 : ScAddress aTmpOrg;
607 : ScBaseCell* pCell;
608 0 : ScAddress aAdr( aOrg );
609 0 : aAdr.IncCol();
610 0 : bool bCont = true;
611 0 : do
612 : {
613 0 : pCell = pDocument->GetCell( aAdr );
614 0 : if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
615 0 : && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
616 0 : && ((ScFormulaCell*)pCell)->GetMatrixOrigin( aTmpOrg )
617 0 : && aTmpOrg == aOrg )
618 : {
619 0 : nC++;
620 0 : aAdr.IncCol();
621 : }
622 : else
623 0 : bCont = false;
624 : } while ( bCont );
625 0 : aAdr = aOrg;
626 0 : aAdr.IncRow();
627 0 : bCont = true;
628 0 : do
629 : {
630 0 : pCell = pDocument->GetCell( aAdr );
631 0 : if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
632 0 : && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
633 0 : && ((ScFormulaCell*)pCell)->GetMatrixOrigin( aTmpOrg )
634 0 : && aTmpOrg == aOrg )
635 : {
636 0 : nR++;
637 0 : aAdr.IncRow();
638 : }
639 : else
640 0 : bCont = false;
641 : } while ( bCont );
642 0 : pFCell->SetMatColsRows( nC, nR );
643 : }
644 : }
645 : else
646 : {
647 : #if OSL_DEBUG_LEVEL > 0
648 : rtl::OUString aTmp;
649 : rtl::OStringBuffer aMsg(RTL_CONSTASCII_STRINGPARAM(
650 : "broken Matrix, no MatFormula at origin, Pos: "));
651 : aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
652 : aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
653 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
654 : aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
655 : aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
656 : OSL_FAIL(aMsg.getStr());
657 : #endif
658 0 : return 0; // bad luck ...
659 : }
660 : }
661 : // here we are, healthy and clean, somewhere in between
662 0 : SCsCOL dC = aPos.Col() - aOrg.Col();
663 0 : SCsROW dR = aPos.Row() - aOrg.Row();
664 0 : sal_uInt16 nEdges = 0;
665 0 : if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
666 : {
667 0 : if ( dC == 0 )
668 0 : nEdges |= 4; // left edge
669 0 : if ( dC+1 == nC )
670 0 : nEdges |= 16; // right edge
671 0 : if ( dR == 0 )
672 0 : nEdges |= 8; // top edge
673 0 : if ( dR+1 == nR )
674 0 : nEdges |= 2; // bottom edge
675 0 : if ( !nEdges )
676 0 : nEdges = 1; // inside
677 : }
678 : #if OSL_DEBUG_LEVEL > 0
679 : else
680 : {
681 : rtl::OUString aTmp;
682 : rtl::OStringBuffer aMsg( "broken Matrix, Pos: " );
683 : aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
684 : aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
685 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
686 : aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
687 : aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
688 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatCols: "));
689 : aMsg.append(static_cast<sal_Int32>( nC ));
690 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatRows: "));
691 : aMsg.append(static_cast<sal_Int32>( nR ));
692 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffCols: "));
693 : aMsg.append(static_cast<sal_Int32>( dC ));
694 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffRows: "));
695 : aMsg.append(static_cast<sal_Int32>( dR ));
696 : OSL_FAIL( aMsg.makeStringAndClear().getStr());
697 : }
698 : #endif
699 0 : return nEdges;
700 : // break;
701 : }
702 : default:
703 0 : return 0;
704 : }
705 : }
706 :
707 3177 : sal_uInt16 ScFormulaCell::GetErrCode()
708 : {
709 3177 : MaybeInterpret();
710 :
711 : /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
712 : * and not also abused for signaling other error conditions we could bail
713 : * out even before attempting to interpret broken code. */
714 3177 : sal_uInt16 nErr = pCode->GetCodeError();
715 3177 : if (nErr)
716 0 : return nErr;
717 3177 : return aResult.GetResultError();
718 : }
719 :
720 0 : sal_uInt16 ScFormulaCell::GetRawError()
721 : {
722 0 : sal_uInt16 nErr = pCode->GetCodeError();
723 0 : if (nErr)
724 0 : return nErr;
725 0 : return aResult.GetResultError();
726 : }
727 :
728 0 : bool ScFormulaCell::HasOneReference( ScRange& r ) const
729 : {
730 0 : pCode->Reset();
731 0 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
732 0 : if( p && !pCode->GetNextReferenceRPN() ) // only one!
733 : {
734 0 : p->CalcAbsIfRel( aPos );
735 0 : SingleDoubleRefProvider aProv( *p );
736 : r.aStart.Set( aProv.Ref1.nCol,
737 : aProv.Ref1.nRow,
738 0 : aProv.Ref1.nTab );
739 : r.aEnd.Set( aProv.Ref2.nCol,
740 : aProv.Ref2.nRow,
741 0 : aProv.Ref2.nTab );
742 0 : return true;
743 : }
744 : else
745 0 : return false;
746 : }
747 :
748 : bool
749 0 : ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
750 : {
751 : /* If there appears just one reference in the formula, it's the same
752 : as HasOneReference(). If there are more of them, they can denote
753 : one range if they are (sole) arguments of one function.
754 : Union of these references must form one range and their
755 : intersection must be empty set.
756 : */
757 :
758 : // Detect the simple case of exactly one reference in advance without all
759 : // overhead.
760 : // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
761 : // work again, where the function does not have only references.
762 0 : if (HasOneReference( rRange))
763 0 : return true;
764 :
765 0 : pCode->Reset();
766 : // Get first reference, if any
767 : ScToken* const pFirstReference(
768 0 : dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN()));
769 0 : if (pFirstReference)
770 : {
771 : // Collect all consecutive references, starting by the one
772 : // already found
773 0 : std::deque<ScToken*> aReferences;
774 0 : aReferences.push_back(pFirstReference);
775 0 : FormulaToken* pToken(pCode->NextRPN());
776 0 : FormulaToken* pFunction(0);
777 0 : while (pToken)
778 : {
779 0 : if (lcl_isReference(*pToken))
780 : {
781 0 : aReferences.push_back(dynamic_cast<ScToken*>(pToken));
782 0 : pToken = pCode->NextRPN();
783 : }
784 : else
785 : {
786 0 : if (pToken->IsFunction())
787 : {
788 0 : pFunction = pToken;
789 : }
790 0 : break;
791 : }
792 : }
793 0 : if (pFunction && !pCode->GetNextReferenceRPN()
794 0 : && (pFunction->GetParamCount() == aReferences.size()))
795 : {
796 0 : return lcl_refListFormsOneRange(aPos, aReferences, rRange);
797 0 : }
798 : }
799 0 : return false;
800 : }
801 :
802 20 : bool ScFormulaCell::HasRelNameReference() const
803 : {
804 20 : pCode->Reset();
805 : ScToken* t;
806 20 : while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
807 : {
808 49 : if ( t->GetSingleRef().IsRelName() ||
809 24 : (t->GetType() == formula::svDoubleRef &&
810 1 : t->GetDoubleRef().Ref2.IsRelName()) )
811 0 : return true;
812 : }
813 20 : return false;
814 : }
815 :
816 0 : bool ScFormulaCell::HasColRowName() const
817 : {
818 0 : pCode->Reset();
819 0 : return (pCode->GetNextColRowName() != NULL);
820 : }
821 :
822 16 : bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode,
823 : const ScRange& r,
824 : SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
825 : ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
826 : {
827 16 : bool bCellStateChanged = false;
828 :
829 16 : SCCOL nCol1 = r.aStart.Col();
830 16 : SCROW nRow1 = r.aStart.Row();
831 16 : SCTAB nTab1 = r.aStart.Tab();
832 16 : SCCOL nCol2 = r.aEnd.Col();
833 16 : SCROW nRow2 = r.aEnd.Row();
834 16 : SCTAB nTab2 = r.aEnd.Tab();
835 16 : SCCOL nCol = aPos.Col();
836 16 : SCROW nRow = aPos.Row();
837 16 : SCTAB nTab = aPos.Tab();
838 16 : ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
839 16 : if ( pUndoCellPos )
840 4 : aUndoPos = *pUndoCellPos;
841 16 : ScAddress aOldPos( aPos );
842 : // bool bPosChanged = false; // if this cell was moved
843 16 : bool bIsInsert = false;
844 16 : if (eUpdateRefMode == URM_INSDEL)
845 : {
846 4 : bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0);
847 4 : if ( nDx && nRow >= nRow1 && nRow <= nRow2 &&
848 : nTab >= nTab1 && nTab <= nTab2 )
849 : {
850 0 : if (nCol >= nCol1)
851 : {
852 0 : nCol = sal::static_int_cast<SCCOL>( nCol + nDx );
853 0 : if ((SCsCOL) nCol < 0)
854 0 : nCol = 0;
855 0 : else if ( nCol > MAXCOL )
856 0 : nCol = MAXCOL;
857 0 : bCellStateChanged = aPos.Col() != nCol;
858 0 : aPos.SetCol( nCol );
859 : }
860 : }
861 4 : if ( nDy && nCol >= nCol1 && nCol <= nCol2 &&
862 : nTab >= nTab1 && nTab <= nTab2 )
863 : {
864 0 : if (nRow >= nRow1)
865 : {
866 0 : nRow = sal::static_int_cast<SCROW>( nRow + nDy );
867 0 : if ((SCsROW) nRow < 0)
868 0 : nRow = 0;
869 0 : else if ( nRow > MAXROW )
870 0 : nRow = MAXROW;
871 0 : bCellStateChanged = aPos.Row() != nRow;
872 0 : aPos.SetRow( nRow );
873 : }
874 : }
875 4 : if ( nDz && nCol >= nCol1 && nCol <= nCol2 &&
876 : nRow >= nRow1 && nRow <= nRow2 )
877 : {
878 0 : if (nTab >= nTab1)
879 : {
880 0 : SCTAB nMaxTab = pDocument->GetTableCount() - 1;
881 0 : nTab = sal::static_int_cast<SCTAB>( nTab + nDz );
882 0 : if ((SCsTAB) nTab < 0)
883 0 : nTab = 0;
884 0 : else if ( nTab > nMaxTab )
885 0 : nTab = nMaxTab;
886 0 : bCellStateChanged = aPos.Tab() != nTab;
887 0 : aPos.SetTab( nTab );
888 : }
889 : }
890 : }
891 12 : else if ( r.In( aPos ) )
892 : {
893 12 : aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
894 : }
895 :
896 16 : bool bHasRefs = false;
897 16 : bool bHasColRowNames = false;
898 16 : bool bOnRefMove = false;
899 16 : if ( !pDocument->IsClipOrUndo() )
900 : {
901 16 : pCode->Reset();
902 16 : bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
903 16 : if ( !bHasRefs || eUpdateRefMode == URM_COPY )
904 : {
905 12 : pCode->Reset();
906 12 : bHasColRowNames = (pCode->GetNextColRowName() != NULL);
907 12 : bHasRefs = bHasRefs || bHasColRowNames;
908 : }
909 16 : bOnRefMove = pCode->IsRecalcModeOnRefMove();
910 : }
911 16 : if( bHasRefs || bOnRefMove )
912 : {
913 16 : ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
914 : ScRangeData* pRangeData;
915 16 : bool bValChanged = false;
916 16 : bool bRangeModified = false; // any range, not only shared formula
917 16 : bool bRefSizeChanged = false;
918 16 : if ( bHasRefs )
919 : {
920 16 : ScCompiler aComp(pDocument, aPos, *pCode);
921 16 : aComp.SetGrammar(pDocument->GetGrammar());
922 : pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r,
923 : nDx, nDy, nDz,
924 16 : bValChanged, bRefSizeChanged);
925 16 : bRangeModified = aComp.HasModifiedRange();
926 : }
927 : else
928 : {
929 0 : bValChanged = false;
930 0 : pRangeData = NULL;
931 0 : bRangeModified = false;
932 0 : bRefSizeChanged = false;
933 : }
934 :
935 16 : bCellStateChanged |= bValChanged;
936 :
937 16 : if ( bOnRefMove )
938 2 : bOnRefMove = (bValChanged || (aPos != aOldPos));
939 : // Cell may reference itself, e.g. ocColumn, ocRow without parameter
940 :
941 : bool bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo;
942 16 : if ( bHasRefs )
943 : {
944 : // Upon Insert ColRowNames have to be recompiled in case the
945 : // insertion occurs right in front of the range.
946 : bColRowNameCompile =
947 16 : (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0));
948 16 : if ( bColRowNameCompile )
949 : {
950 4 : bColRowNameCompile = false;
951 : ScToken* t;
952 4 : ScRangePairList* pColList = pDocument->GetColNameRanges();
953 4 : ScRangePairList* pRowList = pDocument->GetRowNameRanges();
954 4 : pCode->Reset();
955 8 : while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL )
956 : {
957 0 : ScSingleRefData& rRef = t->GetSingleRef();
958 0 : if ( nDy > 0 && rRef.IsColRel() )
959 : { // ColName
960 0 : rRef.CalcAbsIfRel( aPos );
961 0 : ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
962 0 : ScRangePair* pR = pColList->Find( aAdr );
963 0 : if ( pR )
964 : { // defined
965 0 : if ( pR->GetRange(1).aStart.Row() == nRow1 )
966 0 : bColRowNameCompile = true;
967 : }
968 : else
969 : { // on the fly
970 0 : if ( rRef.nRow + 1 == nRow1 )
971 0 : bColRowNameCompile = true;
972 : }
973 : }
974 0 : if ( nDx > 0 && rRef.IsRowRel() )
975 : { // RowName
976 0 : rRef.CalcAbsIfRel( aPos );
977 0 : ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
978 0 : ScRangePair* pR = pRowList->Find( aAdr );
979 0 : if ( pR )
980 : { // defined
981 0 : if ( pR->GetRange(1).aStart.Col() == nCol1 )
982 0 : bColRowNameCompile = true;
983 : }
984 : else
985 : { // on the fly
986 0 : if ( rRef.nCol + 1 == nCol1 )
987 0 : bColRowNameCompile = true;
988 : }
989 : }
990 : }
991 : }
992 12 : else if ( eUpdateRefMode == URM_MOVE )
993 : { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde
994 : // oder diese Zelle auf einen zeigt und verschoben wurde
995 : // During Move/D&D was recompiled, when ColRowName had been delayed
996 0 : bColRowNameCompile = bCompile; // evtl. aus Copy-ctor
997 0 : if ( !bColRowNameCompile )
998 : {
999 0 : bool bMoved = (aPos != aOldPos);
1000 0 : pCode->Reset();
1001 0 : ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
1002 0 : if ( t && bMoved )
1003 0 : bColRowNameCompile = true;
1004 0 : while ( t && !bColRowNameCompile )
1005 : {
1006 0 : ScSingleRefData& rRef = t->GetSingleRef();
1007 0 : rRef.CalcAbsIfRel( aPos );
1008 0 : if ( rRef.Valid() )
1009 : {
1010 0 : ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
1011 0 : if ( r.In( aAdr ) )
1012 0 : bColRowNameCompile = true;
1013 : }
1014 0 : t = static_cast<ScToken*>(pCode->GetNextColRowName());
1015 : }
1016 : }
1017 : }
1018 12 : else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
1019 : {
1020 0 : bColRowNameCompile = true;
1021 : }
1022 16 : ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
1023 16 : if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1024 0 : bInDeleteUndo = true;
1025 : else
1026 16 : bInDeleteUndo = false;
1027 : // RelNameRefs are always moved
1028 16 : bHasRelName = HasRelNameReference();
1029 : // Reference changed and new listening needed?
1030 : // Except in Insert/Delete without specialties.
1031 : bNewListening = (bRangeModified || pRangeData || bColRowNameCompile
1032 : || (bValChanged && (eUpdateRefMode != URM_INSDEL ||
1033 : bInDeleteUndo || bRefSizeChanged)) ||
1034 : (bHasRelName && eUpdateRefMode != URM_COPY))
1035 : // #i36299# Don't duplicate action during cut&paste / drag&drop
1036 : // on a cell in the range moved, start/end listeners is done
1037 : // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
1038 : && !(eUpdateRefMode == URM_MOVE &&
1039 16 : pDocument->IsInsertingFromOtherDoc() && r.In(aPos));
1040 16 : if ( bNewListening )
1041 0 : EndListeningTo( pDocument, pOld, aOldPos );
1042 : }
1043 : else
1044 : {
1045 : bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo =
1046 0 : false;
1047 : }
1048 :
1049 16 : bool bNeedDirty = false;
1050 : // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames
1051 18 : if ( bRangeModified || pRangeData || bColRowNameCompile ||
1052 : (bValChanged && eUpdateRefMode != URM_COPY &&
1053 : (eUpdateRefMode != URM_MOVE || bHasRelName) &&
1054 2 : (!bIsInsert || bHasRelName || bInDeleteUndo ||
1055 : bRefSizeChanged)) || bOnRefMove)
1056 2 : bNeedDirty = true;
1057 : else
1058 14 : bNeedDirty = false;
1059 16 : if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove))
1060 : {
1061 : // Copy the cell to aUndoPos, which is its current position in the document,
1062 : // so this works when UpdateReference is called before moving the cells
1063 : // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
1064 : // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
1065 :
1066 : // If there is already a formula cell in the undo document, don't overwrite it,
1067 : // the first (oldest) is the important cell.
1068 0 : if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA )
1069 : {
1070 : ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos,
1071 0 : pOld, eTempGrammar, cMatrixFlag );
1072 0 : pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
1073 0 : pUndoDoc->PutCell( aUndoPos, pFCell );
1074 : }
1075 : }
1076 16 : bValChanged = false;
1077 16 : if ( pRangeData )
1078 : { // Replace shared formula with own formula
1079 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
1080 0 : delete pCode;
1081 0 : pCode = pRangeData->GetCode()->Clone();
1082 : // #i18937# #i110008# call MoveRelWrap, but with the old position
1083 0 : ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1084 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
1085 0 : aComp2.SetGrammar(pDocument->GetGrammar());
1086 : aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r,
1087 0 : nDx, nDy, nDz );
1088 0 : bValChanged = true;
1089 0 : bNeedDirty = true;
1090 : }
1091 16 : if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
1092 : {
1093 0 : CompileTokenArray( bNewListening ); // no Listening
1094 0 : bNeedDirty = true;
1095 : }
1096 16 : if ( !bInDeleteUndo )
1097 : { // In ChangeTrack Delete-Reject listeners are established in
1098 : // InsertCol/InsertRow
1099 16 : if ( bNewListening )
1100 : {
1101 0 : if ( eUpdateRefMode == URM_INSDEL )
1102 : {
1103 : // Inserts/Deletes re-establish listeners after all
1104 : // UpdateReference calls.
1105 : // All replaced shared formula listeners have to be
1106 : // established after an Insert or Delete. Do nothing here.
1107 0 : SetNeedsListening( true);
1108 : }
1109 : else
1110 0 : StartListeningTo( pDocument );
1111 : }
1112 : }
1113 16 : if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) )
1114 : { // Referenzen abgeschnitten, ungueltig o.ae.?
1115 2 : bool bOldAutoCalc = pDocument->GetAutoCalc();
1116 : // no Interpret in SubMinimalRecalc because of evntual wrong reference
1117 2 : pDocument->SetAutoCalc( false );
1118 2 : SetDirty();
1119 2 : pDocument->SetAutoCalc( bOldAutoCalc );
1120 : }
1121 :
1122 16 : delete pOld;
1123 : }
1124 16 : return bCellStateChanged;
1125 : }
1126 :
1127 6 : void ScFormulaCell::UpdateInsertTab(SCTAB nTable, SCTAB nNewSheets)
1128 : {
1129 6 : bool bPosChanged = ( aPos.Tab() >= nTable ? true : false );
1130 6 : pCode->Reset();
1131 6 : if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1132 : {
1133 6 : EndListeningTo( pDocument );
1134 : // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab !
1135 6 : if ( bPosChanged )
1136 6 : aPos.IncTab(nNewSheets);
1137 : ScRangeData* pRangeData;
1138 6 : ScCompiler aComp(pDocument, aPos, *pCode);
1139 6 : aComp.SetGrammar(pDocument->GetGrammar());
1140 6 : pRangeData = aComp.UpdateInsertTab( nTable, false, nNewSheets );
1141 6 : if (pRangeData) // Shared Formula against real Formula
1142 : { // exchange
1143 : bool bRefChanged;
1144 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
1145 0 : delete pCode;
1146 0 : pCode = new ScTokenArray( *pRangeData->GetCode() );
1147 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
1148 0 : aComp2.SetGrammar(pDocument->GetGrammar());
1149 0 : aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1150 0 : aComp2.UpdateInsertTab( nTable, false, nNewSheets );
1151 : // If the shared formula contained a named range/formula containing
1152 : // an absolute reference to a sheet, those have to be readjusted.
1153 0 : aComp2.UpdateDeleteTab( nTable, false, true, bRefChanged, nNewSheets );
1154 0 : bCompile = true;
1155 6 : }
1156 : // no StartListeningTo becuase pTab[nTab] does not exsist!
1157 : }
1158 0 : else if ( bPosChanged )
1159 0 : aPos.IncTab();
1160 6 : }
1161 :
1162 55 : bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, bool bIsMove, SCTAB nSheets)
1163 : {
1164 55 : bool bRefChanged = false;
1165 55 : bool bPosChanged = ( aPos.Tab() >= nTable + nSheets ? true : false );
1166 55 : pCode->Reset();
1167 55 : if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1168 : {
1169 54 : EndListeningTo( pDocument );
1170 : // IncTab _after_ EndListeningTo und _before_ Compiler UpdateDeleteTab !
1171 54 : if ( bPosChanged )
1172 12 : aPos.IncTab(-1*nSheets);
1173 : ScRangeData* pRangeData;
1174 54 : ScCompiler aComp(pDocument, aPos, *pCode);
1175 54 : aComp.SetGrammar(pDocument->GetGrammar());
1176 54 : pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, false, bRefChanged, nSheets);
1177 54 : if (pRangeData) // Shared Formula against real Formula
1178 : { // exchange
1179 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
1180 0 : delete pCode;
1181 0 : pCode = pRangeData->GetCode()->Clone();
1182 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
1183 0 : aComp2.SetGrammar(pDocument->GetGrammar());
1184 0 : aComp2.CompileTokenArray();
1185 0 : aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1186 0 : aComp2.UpdateDeleteTab( nTable, false, false, bRefChanged, nSheets );
1187 : // If the shared formula contained a named range/formula containing
1188 : // an absolute reference to a sheet, those have to be readjusted.
1189 0 : aComp2.UpdateInsertTab( nTable,true, nSheets );
1190 : // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein
1191 0 : bRefChanged = true;
1192 0 : bCompile = true;
1193 54 : }
1194 : // no StartListeningTo because pTab[nTab] not yet correct!
1195 : }
1196 1 : else if ( bPosChanged )
1197 0 : aPos.IncTab(-1*nSheets);
1198 :
1199 55 : return bRefChanged;
1200 : }
1201 :
1202 0 : void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
1203 : {
1204 0 : pCode->Reset();
1205 0 : if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1206 : {
1207 0 : EndListeningTo( pDocument );
1208 : // SetTab _after_ EndListeningTo und _before_ Compiler UpdateMoveTab !
1209 0 : aPos.SetTab( nTabNo );
1210 : ScRangeData* pRangeData;
1211 0 : ScCompiler aComp(pDocument, aPos, *pCode);
1212 0 : aComp.SetGrammar(pDocument->GetGrammar());
1213 0 : pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, false );
1214 0 : if (pRangeData) // Shared Formula gegen echte Formel
1215 : { // exchange
1216 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
1217 0 : delete pCode;
1218 0 : pCode = pRangeData->GetCode()->Clone();
1219 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
1220 0 : aComp2.SetGrammar(pDocument->GetGrammar());
1221 0 : aComp2.CompileTokenArray();
1222 0 : aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1223 0 : aComp2.UpdateMoveTab( nOldPos, nNewPos, true );
1224 0 : bCompile = true;
1225 0 : }
1226 : // no StartListeningTo because pTab[nTab] not yet correct!
1227 : }
1228 : else
1229 0 : aPos.SetTab( nTabNo );
1230 0 : }
1231 :
1232 0 : void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
1233 : {
1234 0 : if( !pDocument->IsClipOrUndo() )
1235 : {
1236 0 : pCode->Reset();
1237 0 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1238 0 : while( p )
1239 : {
1240 0 : ScSingleRefData& rRef1 = p->GetSingleRef();
1241 0 : if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab )
1242 0 : rRef1.nTab++;
1243 0 : if( p->GetType() == formula::svDoubleRef )
1244 : {
1245 0 : ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
1246 0 : if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab )
1247 0 : rRef2.nTab++;
1248 : }
1249 0 : p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1250 : }
1251 : }
1252 0 : }
1253 :
1254 0 : bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
1255 : {
1256 0 : bool bRet = false;
1257 0 : if( !pDocument->IsClipOrUndo() )
1258 : {
1259 0 : pCode->Reset();
1260 0 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1261 0 : while( p )
1262 : {
1263 0 : ScSingleRefData& rRef1 = p->GetSingleRef();
1264 0 : if( !rRef1.IsTabRel() )
1265 : {
1266 0 : if( (SCsTAB) nTable != rRef1.nTab )
1267 0 : bRet = true;
1268 0 : else if (nTable != aPos.Tab())
1269 0 : rRef1.nTab = aPos.Tab();
1270 : }
1271 0 : if( p->GetType() == formula::svDoubleRef )
1272 : {
1273 0 : ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
1274 0 : if( !rRef2.IsTabRel() )
1275 : {
1276 0 : if( (SCsTAB) nTable != rRef2.nTab )
1277 0 : bRet = true;
1278 0 : else if (nTable != aPos.Tab())
1279 0 : rRef2.nTab = aPos.Tab();
1280 : }
1281 : }
1282 0 : p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1283 : }
1284 : }
1285 0 : return bRet;
1286 : }
1287 :
1288 53 : void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
1289 : {
1290 53 : if ( bForceIfNameInUse && !bCompile )
1291 0 : bCompile = pCode->HasNameOrColRowName();
1292 53 : if ( bCompile )
1293 0 : pCode->SetCodeError( 0 ); // make sure it will really be compiled
1294 53 : CompileTokenArray();
1295 53 : }
1296 :
1297 : // Reference transposition is only called in Clipboard Document
1298 0 : void ScFormulaCell::TransposeReference()
1299 : {
1300 0 : bool bFound = false;
1301 0 : pCode->Reset();
1302 : ScToken* t;
1303 0 : while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
1304 : {
1305 0 : ScSingleRefData& rRef1 = t->GetSingleRef();
1306 0 : if ( rRef1.IsColRel() && rRef1.IsRowRel() )
1307 : {
1308 0 : bool bDouble = (t->GetType() == formula::svDoubleRef);
1309 0 : ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1);
1310 0 : if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
1311 : {
1312 : sal_Int16 nTemp;
1313 :
1314 0 : nTemp = rRef1.nRelCol;
1315 0 : rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow);
1316 0 : rRef1.nRelRow = static_cast<SCROW>(nTemp);
1317 :
1318 0 : if ( bDouble )
1319 : {
1320 0 : nTemp = rRef2.nRelCol;
1321 0 : rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
1322 0 : rRef2.nRelRow = static_cast<SCROW>(nTemp);
1323 : }
1324 :
1325 0 : bFound = true;
1326 : }
1327 : }
1328 : }
1329 :
1330 0 : if (bFound)
1331 0 : bCompile = true;
1332 0 : }
1333 :
1334 0 : void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
1335 : ScDocument* pUndoDoc )
1336 : {
1337 0 : EndListeningTo( pDocument );
1338 :
1339 0 : ScAddress aOldPos = aPos;
1340 0 : bool bPosChanged = false; // If this cell has been moved
1341 :
1342 : ScRange aDestRange( rDest, ScAddress(
1343 0 : static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
1344 0 : static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
1345 0 : rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
1346 0 : if ( aDestRange.In( aOldPos ) )
1347 : {
1348 : // Count back Positions
1349 0 : SCsCOL nRelPosX = aOldPos.Col();
1350 0 : SCsROW nRelPosY = aOldPos.Row();
1351 0 : SCsTAB nRelPosZ = aOldPos.Tab();
1352 0 : ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
1353 0 : aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
1354 0 : bPosChanged = true;
1355 : }
1356 :
1357 0 : ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
1358 0 : bool bRefChanged = false;
1359 : ScToken* t;
1360 :
1361 0 : ScRangeData* pShared = NULL;
1362 0 : pCode->Reset();
1363 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
1364 : {
1365 0 : if( t->GetOpCode() == ocName )
1366 : {
1367 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
1368 0 : if (pName)
1369 : {
1370 0 : if (pName->IsModified())
1371 0 : bRefChanged = true;
1372 0 : if (pName->HasType(RT_SHAREDMOD))
1373 0 : pShared = pName;
1374 : }
1375 : }
1376 0 : else if( t->GetType() != svIndex )
1377 : {
1378 0 : t->CalcAbsIfRel( aOldPos );
1379 : bool bMod;
1380 : { // own scope for SingleDoubleRefModifier dtor if SingleRef
1381 0 : SingleDoubleRefModifier aMod( *t );
1382 0 : ScComplexRefData& rRef = aMod.Ref();
1383 : bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
1384 0 : rDest, rRef ) != UR_NOTHING || bPosChanged);
1385 : }
1386 0 : if ( bMod )
1387 : {
1388 0 : t->CalcRelFromAbs( aPos );
1389 0 : bRefChanged = true;
1390 : }
1391 : }
1392 : }
1393 :
1394 0 : if (pShared) // Shared Formula against real Formula exchange
1395 : {
1396 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
1397 0 : delete pCode;
1398 0 : pCode = new ScTokenArray( *pShared->GetCode() );
1399 0 : bRefChanged = true;
1400 0 : pCode->Reset();
1401 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
1402 : {
1403 0 : if( t->GetType() != svIndex )
1404 : {
1405 0 : t->CalcAbsIfRel( aOldPos );
1406 : bool bMod;
1407 : { // own scope for SingleDoubleRefModifier dtor if SingleRef
1408 0 : SingleDoubleRefModifier aMod( *t );
1409 0 : ScComplexRefData& rRef = aMod.Ref();
1410 : bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
1411 0 : rDest, rRef ) != UR_NOTHING || bPosChanged);
1412 : }
1413 0 : if ( bMod )
1414 0 : t->CalcRelFromAbs( aPos );
1415 : }
1416 : }
1417 : }
1418 :
1419 0 : if (bRefChanged)
1420 : {
1421 0 : if (pUndoDoc)
1422 : {
1423 : ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld,
1424 0 : eTempGrammar, cMatrixFlag);
1425 0 : pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
1426 0 : pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell );
1427 : }
1428 :
1429 0 : bCompile = true;
1430 0 : CompileTokenArray(); // also call StartListeningTo
1431 0 : SetDirty();
1432 : }
1433 : else
1434 0 : StartListeningTo( pDocument ); // Listener as previous
1435 :
1436 0 : delete pOld;
1437 0 : }
1438 :
1439 0 : void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
1440 : {
1441 0 : EndListeningTo( pDocument );
1442 :
1443 0 : bool bRefChanged = false;
1444 : ScToken* t;
1445 0 : ScRangeData* pShared = NULL;
1446 :
1447 0 : pCode->Reset();
1448 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
1449 : {
1450 0 : if( t->GetOpCode() == ocName )
1451 : {
1452 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
1453 0 : if (pName)
1454 : {
1455 0 : if (pName->IsModified())
1456 0 : bRefChanged = true;
1457 0 : if (pName->HasType(RT_SHAREDMOD))
1458 0 : pShared = pName;
1459 : }
1460 : }
1461 0 : else if( t->GetType() != svIndex )
1462 : {
1463 0 : t->CalcAbsIfRel( aPos );
1464 : bool bMod;
1465 : { // own scope for SingleDoubleRefModifier dtor if SingleRef
1466 0 : SingleDoubleRefModifier aMod( *t );
1467 0 : ScComplexRefData& rRef = aMod.Ref();
1468 : bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
1469 0 : rRef ) != UR_NOTHING);
1470 : }
1471 0 : if ( bMod )
1472 : {
1473 0 : t->CalcRelFromAbs( aPos );
1474 0 : bRefChanged = true;
1475 : }
1476 : }
1477 : }
1478 :
1479 0 : if (pShared) // Shared Formula gegen echte Formel austauschen
1480 : {
1481 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
1482 0 : delete pCode;
1483 0 : pCode = new ScTokenArray( *pShared->GetCode() );
1484 0 : bRefChanged = true;
1485 0 : pCode->Reset();
1486 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
1487 : {
1488 0 : if( t->GetType() != svIndex )
1489 : {
1490 0 : t->CalcAbsIfRel( aPos );
1491 : bool bMod;
1492 : { // own scope for SingleDoubleRefModifier dtor if SingleRef
1493 0 : SingleDoubleRefModifier aMod( *t );
1494 0 : ScComplexRefData& rRef = aMod.Ref();
1495 : bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
1496 0 : rRef ) != UR_NOTHING);
1497 : }
1498 0 : if ( bMod )
1499 0 : t->CalcRelFromAbs( aPos );
1500 : }
1501 : }
1502 : }
1503 :
1504 0 : if (bRefChanged)
1505 : {
1506 0 : bCompile = true;
1507 0 : CompileTokenArray(); // also call StartListeningTo
1508 0 : SetDirty();
1509 : }
1510 : else
1511 0 : StartListeningTo( pDocument ); // Listener as previous
1512 0 : }
1513 :
1514 3 : static void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
1515 : {
1516 13 : for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
1517 : {
1518 10 : if (p->GetOpCode() == ocName)
1519 : {
1520 2 : sal_uInt16 nTokenIndex = p->GetIndex();
1521 2 : rIndexes.insert( nTokenIndex );
1522 :
1523 2 : ScRangeData* pSubName = pNames->findByIndex(p->GetIndex());
1524 2 : if (pSubName)
1525 2 : lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
1526 : }
1527 : }
1528 3 : }
1529 :
1530 1 : void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
1531 : {
1532 1 : lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
1533 1 : }
1534 :
1535 0 : bool ScFormulaCell::IsChanged() const
1536 : {
1537 0 : return bChanged;
1538 : }
1539 :
1540 23 : void ScFormulaCell::ResetChanged()
1541 : {
1542 23 : bChanged = false;
1543 23 : }
1544 :
1545 0 : void ScFormulaCell::CompileDBFormula()
1546 : {
1547 0 : for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1548 : {
1549 0 : if ( p->GetOpCode() == ocDBArea
1550 0 : || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) )
1551 : {
1552 0 : bCompile = true;
1553 0 : CompileTokenArray();
1554 0 : SetDirty();
1555 0 : break;
1556 : }
1557 : }
1558 0 : }
1559 :
1560 0 : void ScFormulaCell::CompileDBFormula( bool bCreateFormulaString )
1561 : {
1562 : // two phases must be called after each other
1563 : // 1. Formula String with old generated names
1564 : // 2. Formula String with new generated names
1565 0 : if ( bCreateFormulaString )
1566 : {
1567 0 : bool bRecompile = false;
1568 0 : pCode->Reset();
1569 0 : for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
1570 : {
1571 0 : switch ( p->GetOpCode() )
1572 : {
1573 : case ocBad: // DB-Area eventually goes bad
1574 : case ocColRowName: // in case of the same names
1575 : case ocDBArea: // DB-Area
1576 0 : bRecompile = true;
1577 0 : break;
1578 : case ocName:
1579 0 : if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
1580 0 : bRecompile = true; // DB-Area
1581 0 : break;
1582 : default:
1583 : ; // nothing
1584 : }
1585 : }
1586 0 : if ( bRecompile )
1587 : {
1588 0 : rtl::OUString aFormula;
1589 0 : GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1590 0 : if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
1591 : {
1592 0 : if ( aFormula[ aFormula.getLength()-1 ] == '}' )
1593 0 : aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
1594 0 : if ( aFormula[0] == '{' )
1595 0 : aFormula = aFormula.copy( 1 );
1596 : }
1597 0 : EndListeningTo( pDocument );
1598 0 : pDocument->RemoveFromFormulaTree( this );
1599 0 : pCode->Clear();
1600 0 : SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1601 : }
1602 : }
1603 0 : else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1604 : {
1605 0 : Compile( aResult.GetHybridFormula(), false, eTempGrammar );
1606 0 : aResult.SetToken( NULL);
1607 0 : SetDirty();
1608 : }
1609 0 : }
1610 :
1611 34 : void ScFormulaCell::CompileNameFormula( bool bCreateFormulaString )
1612 : {
1613 : // two phases must be called after each other
1614 : // 1. Formula String with old generated names
1615 : // 2. Formula String with new generated names
1616 34 : if ( bCreateFormulaString )
1617 : {
1618 0 : bool bRecompile = false;
1619 0 : pCode->Reset();
1620 0 : for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
1621 : {
1622 0 : switch ( p->GetOpCode() )
1623 : {
1624 : case ocBad: // in case RangeName goes bad
1625 : case ocColRowName: // in case the names are the same
1626 0 : bRecompile = true;
1627 0 : break;
1628 : default:
1629 0 : if ( p->GetType() == svIndex )
1630 0 : bRecompile = true; // RangeName
1631 : }
1632 : }
1633 0 : if ( bRecompile )
1634 : {
1635 0 : rtl::OUString aFormula;
1636 0 : GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1637 0 : if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
1638 : {
1639 0 : if ( aFormula[ aFormula.getLength()-1 ] == '}' )
1640 0 : aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
1641 0 : if ( aFormula[0] == '{' )
1642 0 : aFormula = aFormula.copy( 1 );
1643 : }
1644 0 : EndListeningTo( pDocument );
1645 0 : pDocument->RemoveFromFormulaTree( this );
1646 0 : pCode->Clear();
1647 0 : SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1648 : }
1649 : }
1650 34 : else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1651 : {
1652 0 : Compile( aResult.GetHybridFormula(), false, eTempGrammar );
1653 0 : aResult.SetToken( NULL);
1654 0 : SetDirty();
1655 : }
1656 34 : }
1657 :
1658 0 : void ScFormulaCell::CompileColRowNameFormula()
1659 : {
1660 0 : pCode->Reset();
1661 0 : for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1662 : {
1663 0 : if ( p->GetOpCode() == ocColRowName )
1664 : {
1665 0 : bCompile = true;
1666 0 : CompileTokenArray();
1667 0 : SetDirty();
1668 0 : break;
1669 : }
1670 : }
1671 15 : }
1672 :
1673 : // ============================================================================
1674 :
1675 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|