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 :
10 : #include "ucalc.hxx"
11 :
12 : #include <sal/config.h>
13 : #include <test/bootstrapfixture.hxx>
14 :
15 : #include <rtl/strbuf.hxx>
16 : #include <osl/file.hxx>
17 : #include <osl/time.h>
18 :
19 : #include "scdll.hxx"
20 : #include "formulacell.hxx"
21 : #include "stringutil.hxx"
22 : #include "scmatrix.hxx"
23 : #include "drwlayer.hxx"
24 : #include "scitems.hxx"
25 : #include "reffind.hxx"
26 : #include "markdata.hxx"
27 : #include "clipparam.hxx"
28 : #include "refundo.hxx"
29 : #include "undoblk.hxx"
30 : #include "undotab.hxx"
31 : #include "queryentry.hxx"
32 : #include "postit.hxx"
33 : #include "attrib.hxx"
34 : #include "dbdata.hxx"
35 : #include "reftokenhelper.hxx"
36 : #include "userdat.hxx"
37 :
38 : #include "docsh.hxx"
39 : #include "docfunc.hxx"
40 : #include "dbdocfun.hxx"
41 : #include "funcdesc.hxx"
42 : #include "externalrefmgr.hxx"
43 :
44 : #include "calcconfig.hxx"
45 : #include "interpre.hxx"
46 : #include "columniterator.hxx"
47 : #include "types.hxx"
48 : #include "conditio.hxx"
49 : #include "globstr.hrc"
50 : #include "tokenarray.hxx"
51 : #include "scopetools.hxx"
52 : #include "dociter.hxx"
53 : #include "queryparam.hxx"
54 : #include "edittextiterator.hxx"
55 : #include "editutil.hxx"
56 : #include <asciiopt.hxx>
57 : #include <impex.hxx>
58 : #include <columnspanset.hxx>
59 : #include <docoptio.hxx>
60 : #include <patattr.hxx>
61 : #include <docpool.hxx>
62 :
63 : #include "formula/IFunctionDescription.hxx"
64 :
65 : #include <basegfx/polygon/b2dpolygon.hxx>
66 : #include <editeng/boxitem.hxx>
67 : #include <editeng/brushitem.hxx>
68 : #include "editeng/wghtitem.hxx"
69 : #include "editeng/postitem.hxx"
70 :
71 : #include <svx/svdograf.hxx>
72 : #include <svx/svdpage.hxx>
73 : #include <svx/svdocirc.hxx>
74 : #include <svx/svdopath.hxx>
75 : #include <svx/svdocapt.hxx>
76 : #include "svl/srchitem.hxx"
77 : #include "svl/sharedstringpool.hxx"
78 :
79 : #include <sfx2/docfile.hxx>
80 :
81 : #include <iostream>
82 : #include <sstream>
83 : #include <vector>
84 :
85 : #include <com/sun/star/i18n/TransliterationModules.hpp>
86 :
87 282 : struct TestImpl
88 : {
89 : ScDocShellRef m_xDocShell;
90 : };
91 :
92 8 : FormulaGrammarSwitch::FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
93 8 : mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
94 : {
95 8 : mpDoc->SetGrammar(eGrammar);
96 8 : }
97 :
98 8 : FormulaGrammarSwitch::~FormulaGrammarSwitch()
99 : {
100 8 : mpDoc->SetGrammar(meOldGrammar);
101 8 : }
102 :
103 : class MeasureTimeSwitch
104 : {
105 : double& mrDiff;
106 : TimeValue maTimeBefore;
107 : public:
108 0 : MeasureTimeSwitch(double& rDiff) : mrDiff(rDiff)
109 : {
110 0 : mrDiff = 9999.0;
111 0 : osl_getSystemTime(&maTimeBefore);
112 0 : }
113 :
114 0 : ~MeasureTimeSwitch()
115 : {
116 : TimeValue aTimeAfter;
117 0 : osl_getSystemTime(&aTimeAfter);
118 0 : mrDiff = getTimeDiff(aTimeAfter, maTimeBefore);
119 0 : }
120 :
121 0 : double getTimeDiff(const TimeValue& t1, const TimeValue& t2) const
122 : {
123 0 : double tv1 = t1.Seconds;
124 0 : double tv2 = t2.Seconds;
125 0 : tv1 += t1.Nanosec / 1000000000.0;
126 0 : tv2 += t2.Nanosec / 1000000000.0;
127 :
128 0 : return tv1 - tv2;
129 : }
130 : };
131 :
132 141 : Test::Test() :
133 141 : m_pImpl(new TestImpl),
134 282 : m_pDoc(0)
135 : {
136 141 : }
137 :
138 423 : Test::~Test()
139 : {
140 141 : delete m_pImpl;
141 282 : }
142 :
143 37 : ScDocShell& Test::getDocShell()
144 : {
145 37 : return *m_pImpl->m_xDocShell;
146 : }
147 :
148 141 : void Test::setUp()
149 : {
150 141 : BootstrapFixture::setUp();
151 :
152 141 : ScDLL::Init();
153 423 : m_pImpl->m_xDocShell = new ScDocShell(
154 : SFXMODEL_STANDARD |
155 : SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
156 282 : SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
157 :
158 141 : m_pImpl->m_xDocShell->DoInitUnitTest();
159 141 : m_pDoc = m_pImpl->m_xDocShell->GetDocument();
160 141 : }
161 :
162 141 : void Test::tearDown()
163 : {
164 141 : m_pImpl->m_xDocShell.Clear();
165 141 : BootstrapFixture::tearDown();
166 141 : }
167 :
168 : #define PERF_ASSERT(df,scale,time,message) \
169 : do { \
170 : double dfscaled = df / scale; \
171 : if ((dfscaled) >= (time)) \
172 : { \
173 : std::ostringstream os; \
174 : os << message << " took " << dfscaled << " pseudo-cycles (" << df << " real-time seconds), expected: " << time << " pseudo-cycles."; \
175 : CPPUNIT_FAIL(os.str().c_str()); \
176 : } \
177 : } while (false)
178 :
179 0 : void Test::testPerf()
180 : {
181 0 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
182 :
183 : // First do a set of simple operations to try to work out
184 : // how fast (or not) this particular machine is:
185 : double scale;
186 : {
187 0 : MeasureTimeSwitch aTime(scale);
188 0 : for (int i = 0; i < 10000000; ++i)
189 : {
190 : // Bang on the allocator
191 0 : volatile ScRange *pRange = new ScRange (ScAddress (0,0,0));
192 : // Calc does quite a bit of string conversion
193 0 : volatile double it = OUString::number ((double)i/253.0).toDouble();
194 : // Do we have floating point math ?
195 0 : volatile double another = rtl::math::sin (it);
196 0 : (void)another;
197 0 : delete pRange;
198 0 : }
199 : }
200 0 : printf("CPU scale factor %g\n", scale);
201 :
202 : // FIXME: we should check if this already took too long
203 : // and if so not run the perf. tests to have pity on some
204 : // slow ARM machines - I think.
205 :
206 : // to make the numbers more round and helpful,
207 : // but the calculation of scale reasonably precise.
208 0 : scale /= 100000.0;
209 :
210 : double diff;
211 :
212 : // Clearing an already empty sheet should finish in a fraction of a
213 : // second. Flag failure if it takes more than one second. Clearing 100
214 : // columns should be large enough to flag if something goes wrong.
215 : {
216 0 : MeasureTimeSwitch aTime(diff);
217 0 : clearRange(m_pDoc, ScRange(0,0,0,99,MAXROW,0));
218 : }
219 0 : PERF_ASSERT(diff, scale, 1.0, "Clearing an empty sheet");
220 :
221 : {
222 : // Switch to R1C1 to make it easier to input relative references in multiple cells.
223 0 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
224 :
225 : // Insert formulas in B1:B100000. This shouldn't take long, but may take
226 : // close to a second on a slower machine. We don't measure this yet, for
227 : // now.
228 0 : for (SCROW i = 0; i < 100000; ++i)
229 0 : m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
230 :
231 : // Now, Delete B2:B100000. This should complete in a fraction of a second
232 : // (0.06 sec on my machine).
233 : {
234 0 : MeasureTimeSwitch aTime(diff);
235 0 : clearRange(m_pDoc, ScRange(1,1,0,1,99999,0));
236 : }
237 0 : PERF_ASSERT(diff, scale, 2000, "Removal of a large array of formula cells");
238 : }
239 :
240 0 : clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
241 0 : CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
242 0 : CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
243 :
244 : {
245 : // Measure the performance of repeat-pasting a single cell to a large
246 : // cell range, undoing it, and redoing it.
247 :
248 0 : ScAddress aPos(0,0,0);
249 0 : m_pDoc->SetString(aPos, "test");
250 0 : ScMarkData aMark;
251 0 : aMark.SelectOneTable(0);
252 :
253 : // Copy cell A1 to clipboard.
254 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
255 0 : ScClipParam aParam(aPos, false);
256 0 : m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
257 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
258 :
259 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
260 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
261 0 : m_pDoc->CopyToDocument(ScRange(aPos), IDF_CONTENTS, false, pUndoDoc, &aMark);
262 :
263 : // Paste it to A2:A100000, and measure its duration.
264 0 : ScRange aPasteRange(0,1,0,0,99999,0);
265 0 : aMark.SetMarkArea(aPasteRange);
266 :
267 : {
268 0 : MeasureTimeSwitch aTime(diff);
269 0 : m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
270 : }
271 0 : PERF_ASSERT(diff, scale, 1500.0, "Pasting a single cell to A2:A100000");
272 :
273 0 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
274 0 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
275 0 : m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
276 :
277 : // Create an undo object for this.
278 0 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
279 0 : ScUndoPaste aUndo(&getDocShell(), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
280 :
281 : // Make sure it did what it's supposed to do.
282 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
283 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
284 :
285 : {
286 0 : MeasureTimeSwitch aTime(diff);
287 0 : aUndo.Undo();
288 : }
289 0 : PERF_ASSERT(diff, scale, 500.0, "Undoing a pasting of a cell to A2:A100000");
290 :
291 : // Make sure it's really undone.
292 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, m_pDoc->GetCellType(aPos));
293 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
294 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
295 :
296 : // Now redo.
297 : {
298 0 : MeasureTimeSwitch aTime(diff);
299 0 : aUndo.Redo();
300 : }
301 0 : PERF_ASSERT(diff, scale, 1000.0, "Redoing a pasting of a cell to A2:A100000");
302 :
303 : // Make sure it's really redone.
304 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
305 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
306 : }
307 :
308 0 : clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
309 0 : CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
310 0 : CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
311 :
312 : {
313 : // Measure the performance of repeat-pasting 2 adjacent cells to a
314 : // large cell range, undoing it, and redoing it. The bottom one of
315 : // the two source cells is empty.
316 :
317 0 : ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
318 :
319 0 : ScAddress aPos(0,0,0);
320 0 : m_pDoc->SetValue(aPos, 12);
321 0 : ScMarkData aMark;
322 0 : aMark.SetMarkArea(aSrcRange);
323 :
324 : // Copy to clipboard.
325 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
326 0 : ScClipParam aParam(aSrcRange, false);
327 0 : m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
328 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
329 :
330 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
331 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
332 0 : m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
333 :
334 : // Paste it to A3:A100001, and measure its duration.
335 0 : ScRange aPasteRange(0,2,0,0,100000,0);
336 0 : aMark.SetMarkArea(aPasteRange);
337 :
338 : {
339 0 : MeasureTimeSwitch aTime(diff);
340 0 : m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
341 : }
342 0 : PERF_ASSERT(diff, scale, 1000.0, "Pasting A1:A2 to A3:A100001");
343 :
344 0 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
345 0 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
346 0 : m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
347 :
348 : // Create an undo object for this.
349 0 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
350 0 : ScUndoPaste aUndo(&getDocShell(), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
351 :
352 : // Make sure it did what it's supposed to do.
353 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
354 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
355 0 : ScAddress aTmp = aPasteRange.aStart;
356 0 : aTmp.IncRow();
357 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
358 :
359 : {
360 0 : MeasureTimeSwitch aTime(diff);
361 0 : aUndo.Undo();
362 : }
363 0 : PERF_ASSERT(diff, scale, 500.0, "Undoing");
364 :
365 : // Make sure it's really undone.
366 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(aPos));
367 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
368 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
369 :
370 : // Now redo.
371 : {
372 0 : MeasureTimeSwitch aTime(diff);
373 0 : aUndo.Redo();
374 : }
375 0 : PERF_ASSERT(diff, scale, 800.0, "Redoing");
376 :
377 : // Make sure it's really redone.
378 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
379 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
380 : }
381 :
382 0 : clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
383 0 : CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
384 0 : CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
385 :
386 : {
387 : // Measure the performance of repeat-pasting 2 adjacent cells to a
388 : // large cell range, undoing it, and redoing it. The bottom one of
389 : // the two source cells is empty. In this scenario, the non-empty
390 : // cell is a formula cell referencing a cell to the right, which
391 : // inserts a broadcaster to cell it references. So it has a higher
392 : // overhead than the previous scenario.
393 :
394 0 : ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
395 :
396 0 : ScAddress aPos(0,0,0);
397 0 : m_pDoc->SetString(aPos, "=B1");
398 0 : ScMarkData aMark;
399 0 : aMark.SetMarkArea(aSrcRange);
400 :
401 : // Copy to clipboard.
402 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
403 0 : ScClipParam aParam(aSrcRange, false);
404 0 : m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
405 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
406 :
407 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
408 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
409 0 : m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
410 :
411 : // Paste it to A3:A50001, and measure its duration.
412 0 : ScRange aPasteRange(0,2,0,0,50000,0);
413 0 : aMark.SetMarkArea(aPasteRange);
414 :
415 : {
416 0 : MeasureTimeSwitch aTime(diff);
417 0 : m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
418 : }
419 0 : PERF_ASSERT(diff, scale, 2000.0, "Pasting");
420 :
421 0 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
422 0 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
423 0 : m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
424 :
425 : // Create an undo object for this.
426 0 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
427 0 : ScUndoPaste aUndo(&getDocShell(), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
428 :
429 : // Make sure it did what it's supposed to do.
430 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
431 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
432 0 : ScAddress aTmp = aPasteRange.aStart;
433 0 : aTmp.IncRow();
434 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
435 :
436 : #if 0 // TODO: Undo and redo of this scenario is currently not fast enough to be tested reliably.
437 : {
438 : MeasureTimeSwitch aTime(diff);
439 : aUndo.Undo();
440 : }
441 : PERF_ASSERT(diff, scale, 1.0, "Undoing");
442 :
443 : // Make sure it's really undone.
444 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPos));
445 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
446 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
447 :
448 : // Now redo.
449 : {
450 : MeasureTimeSwitch aTime(diff);
451 : aUndo.Redo();
452 : }
453 : PERF_ASSERT(diff, scale, 1.0, "Redoing");
454 :
455 : // Make sure it's really redone.
456 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
457 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
458 : #endif
459 : }
460 :
461 0 : m_pDoc->DeleteTab(0);
462 0 : }
463 :
464 1 : void Test::testCollator()
465 : {
466 1 : CollatorWrapper* p = ScGlobal::GetCollator();
467 1 : sal_Int32 nRes = p->compareString("A", "B");
468 1 : CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
469 1 : }
470 :
471 1 : void Test::testSharedStringPool()
472 : {
473 1 : m_pDoc->InsertTab(0, "foo");
474 :
475 : // Strings that are identical.
476 1 : m_pDoc->SetString(ScAddress(0,0,0), "Andy"); // A1
477 1 : m_pDoc->SetString(ScAddress(0,1,0), "Andy"); // A2
478 1 : m_pDoc->SetString(ScAddress(0,2,0), "Bruce"); // A3
479 1 : m_pDoc->SetString(ScAddress(0,3,0), "andy"); // A4
480 1 : m_pDoc->SetString(ScAddress(0,4,0), "BRUCE"); // A5
481 :
482 : {
483 : // These two shared string objects must go out of scope before the purge test.
484 1 : svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
485 2 : svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(0,1,0));
486 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS1.isValid());
487 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS2.isValid());
488 1 : CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
489 :
490 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(0,2,0));
491 1 : CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
492 :
493 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
494 1 : CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
495 :
496 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
497 1 : CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
498 :
499 : // A3 and A5 should differ but should be equal case-insensitively.
500 1 : aSS1 = m_pDoc->GetSharedString(ScAddress(0,2,0));
501 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
502 1 : CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
503 1 : CPPUNIT_ASSERT_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase() == aSS2.getDataIgnoreCase());
504 :
505 : // A2 and A4 should be equal when ignoring cases.
506 1 : aSS1 = m_pDoc->GetSharedString(ScAddress(0,1,0));
507 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
508 2 : CPPUNIT_ASSERT_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase() == aSS2.getDataIgnoreCase());
509 : }
510 :
511 : // Check the string counts after purging. Purging shouldn't remove any strings in this case.
512 1 : svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
513 1 : rPool.purge();
514 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rPool.getCount());
515 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPool.getCountIgnoreCase());
516 :
517 : // Clear A1 and purge again.
518 1 : clearRange(m_pDoc, ScAddress(0,0,0));
519 1 : rPool.purge();
520 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rPool.getCount());
521 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPool.getCountIgnoreCase());
522 :
523 : // Clear A2 and purge again.
524 1 : clearRange(m_pDoc, ScAddress(0,1,0));
525 1 : rPool.purge();
526 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), rPool.getCount());
527 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPool.getCountIgnoreCase());
528 :
529 : // Clear A3 and purge again.
530 1 : clearRange(m_pDoc, ScAddress(0,2,0));
531 1 : rPool.purge();
532 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPool.getCount());
533 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPool.getCountIgnoreCase());
534 :
535 : // Clear A4 and purge again.
536 1 : clearRange(m_pDoc, ScAddress(0,3,0));
537 1 : rPool.purge();
538 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPool.getCount());
539 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPool.getCountIgnoreCase());
540 :
541 : // Clear A5 and the pool should be completely empty.
542 1 : clearRange(m_pDoc, ScAddress(0,4,0));
543 1 : rPool.purge();
544 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rPool.getCount());
545 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rPool.getCountIgnoreCase());
546 :
547 : // Now, compare string and edit text cells.
548 1 : m_pDoc->SetString(ScAddress(0,0,0), "Andy and Bruce"); // A1
549 1 : ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
550 1 : rEE.SetText("Andy and Bruce");
551 :
552 1 : ESelection aSel;
553 1 : aSel.nStartPara = aSel.nEndPara = 0;
554 :
555 : {
556 : // Set 'Andy' bold.
557 1 : SfxItemSet aItemSet = rEE.GetEmptyItemSet();
558 1 : aSel.nStartPos = 0;
559 1 : aSel.nEndPos = 4;
560 2 : SvxWeightItem aWeight(WEIGHT_BOLD, EE_CHAR_WEIGHT);
561 1 : aItemSet.Put(aWeight);
562 2 : rEE.QuickSetAttribs(aItemSet, aSel);
563 : }
564 :
565 : {
566 : // Set 'Bruce' italic.
567 1 : SfxItemSet aItemSet = rEE.GetEmptyItemSet();
568 2 : SvxPostureItem aItalic(ITALIC_NORMAL, EE_CHAR_ITALIC);
569 1 : aItemSet.Put(aItalic);
570 1 : aSel.nStartPos = 9;
571 1 : aSel.nEndPos = 14;
572 2 : rEE.QuickSetAttribs(aItemSet, aSel);
573 : }
574 :
575 1 : m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject()); // B1
576 :
577 : // These two should be equal.
578 1 : svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
579 2 : svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(1,0,0));
580 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
581 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
582 1 : CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
583 :
584 1 : rEE.SetText("ANDY and BRUCE");
585 1 : m_pDoc->SetEditText(ScAddress(2,0,0), rEE.CreateTextObject()); // C1
586 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
587 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
588 1 : CPPUNIT_ASSERT_MESSAGE("These two should be different when cases are considered.", aSS1.getData() != aSS2.getData());
589 :
590 : // But they should be considered equal when cases are ignored.
591 1 : aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
592 1 : aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
593 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
594 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
595 1 : CPPUNIT_ASSERT_EQUAL(aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
596 :
597 2 : m_pDoc->DeleteTab(0);
598 1 : }
599 :
600 1 : void Test::testSharedStringPoolUndoDoc()
601 : {
602 1 : m_pDoc->InsertTab(0, "Test");
603 :
604 1 : m_pDoc->SetString(ScAddress(0,0,0), "Header");
605 1 : m_pDoc->SetString(ScAddress(0,1,0), "A1");
606 1 : m_pDoc->SetString(ScAddress(0,2,0), "A2");
607 1 : m_pDoc->SetString(ScAddress(0,3,0), "A3");
608 :
609 1 : ScDocument aUndoDoc(SCDOCMODE_UNDO);
610 1 : aUndoDoc.InitUndo(m_pDoc, 0, 0);
611 :
612 : // Copy A1:A4 to the undo document.
613 6 : for (SCROW i = 0; i <= 4; ++i)
614 : {
615 5 : ScAddress aPos(0,i,0);
616 5 : aUndoDoc.SetString(aPos, m_pDoc->GetString(aPos));
617 : }
618 :
619 : // String values in A1:A4 should have identical hash.
620 6 : for (SCROW i = 0; i <= 4; ++i)
621 : {
622 5 : ScAddress aPos(0,i,0);
623 5 : svl::SharedString aSS1 = m_pDoc->GetSharedString(aPos);
624 10 : svl::SharedString aSS2 = aUndoDoc.GetSharedString(aPos);
625 5 : CPPUNIT_ASSERT_MESSAGE("String hash values are not equal.", aSS1.getDataIgnoreCase() == aSS2.getDataIgnoreCase());
626 5 : }
627 :
628 1 : m_pDoc->DeleteTab(0);
629 1 : }
630 :
631 1 : void Test::testRangeList()
632 : {
633 1 : m_pDoc->InsertTab(0, "foo");
634 :
635 1 : ScRangeList aRL;
636 1 : aRL.Append(ScRange(1,1,0,3,10,0));
637 1 : CPPUNIT_ASSERT_MESSAGE("List should have one range.", aRL.size() == 1);
638 1 : const ScRange* p = aRL[0];
639 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
640 1 : CPPUNIT_ASSERT_MESSAGE("Wrong range.", p->aStart == ScAddress(1,1,0) && p->aEnd == ScAddress(3,10,0));
641 :
642 : // TODO: Add more tests here.
643 :
644 1 : m_pDoc->DeleteTab(0);
645 1 : }
646 :
647 1 : void Test::testMarkData()
648 : {
649 1 : ScMarkData aMarkData;
650 :
651 : // Empty mark. Nothing is selected.
652 2 : std::vector<sc::ColRowSpan> aSpans = aMarkData.GetMarkedRowSpans();
653 1 : CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
654 1 : aSpans = aMarkData.GetMarkedColSpans();
655 1 : CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
656 :
657 : // Select B3:F7.
658 1 : aMarkData.SetMarkArea(ScRange(1,2,0,5,6,0));
659 1 : aSpans = aMarkData.GetMarkedRowSpans();
660 1 : CPPUNIT_ASSERT_MESSAGE("There should be one selected row span.", aSpans.size() == 1);
661 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
662 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
663 :
664 1 : aSpans = aMarkData.GetMarkedColSpans();
665 1 : CPPUNIT_ASSERT_MESSAGE("There should be one selected column span.", aSpans.size() == 1);
666 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(1), aSpans[0].mnStart);
667 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
668 :
669 : // Select A11:B13.
670 1 : aMarkData.SetMultiMarkArea(ScRange(0,10,0,1,12,0));
671 1 : aSpans = aMarkData.GetMarkedRowSpans();
672 1 : CPPUNIT_ASSERT_MESSAGE("There should be 2 selected row spans.", aSpans.size() == 2);
673 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
674 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
675 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(10), aSpans[1].mnStart);
676 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[1].mnEnd);
677 :
678 1 : aSpans = aMarkData.GetMarkedColSpans();
679 1 : CPPUNIT_ASSERT_MESSAGE("There should be one selected column span.", aSpans.size() == 1);
680 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
681 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
682 :
683 : // Select C8:C10.
684 1 : aMarkData.SetMultiMarkArea(ScRange(2,7,0,2,9,0));
685 1 : aSpans = aMarkData.GetMarkedRowSpans();
686 1 : CPPUNIT_ASSERT_MESSAGE("There should be one selected row span.", aSpans.size() == 1);
687 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
688 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[0].mnEnd);
689 :
690 1 : aSpans = aMarkData.GetMarkedColSpans();
691 1 : CPPUNIT_ASSERT_MESSAGE("There should be one selected column span.", aSpans.size() == 1);
692 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
693 2 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
694 1 : }
695 :
696 1 : void Test::testInput()
697 : {
698 :
699 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
700 1 : m_pDoc->InsertTab (0, "foo"));
701 1 : OUString test;
702 :
703 1 : m_pDoc->SetString(0, 0, 0, "'10.5");
704 1 : test = m_pDoc->GetString(0, 0, 0);
705 1 : bool bTest = test == "10.5";
706 1 : CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
707 1 : m_pDoc->SetString(0, 0, 0, "'apple'");
708 1 : test = m_pDoc->GetString(0, 0, 0);
709 1 : bTest = test == "'apple'";
710 1 : CPPUNIT_ASSERT_MESSAGE("Text content should have retained the first apostrophe.", bTest);
711 :
712 : // Customized string handling policy.
713 1 : ScSetStringParam aParam;
714 1 : aParam.setTextInput();
715 1 : m_pDoc->SetString(0, 0, 0, "000123", &aParam);
716 1 : test = m_pDoc->GetString(0, 0, 0);
717 1 : CPPUNIT_ASSERT_MESSAGE("Text content should have been treated as string, not number.", test == "000123");
718 :
719 1 : m_pDoc->DeleteTab(0);
720 1 : }
721 :
722 1 : void Test::testDocStatistics()
723 : {
724 1 : SCTAB nStartTabs = m_pDoc->GetTableCount();
725 1 : m_pDoc->InsertTab(0, "Sheet1");
726 1 : CPPUNIT_ASSERT_MESSAGE("Failed to increment sheet count.", m_pDoc->GetTableCount() == nStartTabs+1);
727 1 : m_pDoc->InsertTab(1, "Sheet2");
728 1 : CPPUNIT_ASSERT_MESSAGE("Failed to increment sheet count.", m_pDoc->GetTableCount() == nStartTabs+2);
729 :
730 1 : CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(0), m_pDoc->GetCellCount());
731 1 : m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
732 1 : CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(1), m_pDoc->GetCellCount());
733 1 : m_pDoc->SetValue(ScAddress(2,2,0), 2.5);
734 1 : CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(2), m_pDoc->GetCellCount());
735 1 : m_pDoc->SetString(ScAddress(1,1,1), "Test");
736 1 : CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(3), m_pDoc->GetCellCount());
737 :
738 1 : m_pDoc->DeleteTab(1);
739 1 : CPPUNIT_ASSERT_MESSAGE("Failed to decrement sheet count.", m_pDoc->GetTableCount() == nStartTabs+1);
740 1 : m_pDoc->DeleteTab(0); // This may fail in case there is only one sheet in the document.
741 1 : }
742 :
743 1 : void Test::testDataEntries()
744 : {
745 1 : m_pDoc->InsertTab(0, "Test");
746 :
747 1 : m_pDoc->SetString(ScAddress(0,5,0), "Andy");
748 1 : m_pDoc->SetString(ScAddress(0,6,0), "Bruce");
749 1 : m_pDoc->SetString(ScAddress(0,7,0), "Charlie");
750 1 : m_pDoc->SetString(ScAddress(0,10,0), "Andy");
751 :
752 1 : std::vector<ScTypedStrData> aEntries;
753 1 : m_pDoc->GetDataEntries(0, 0, 0, true, aEntries); // Try at the very top.
754 :
755 : // Entries are supposed to be sorted in ascending order, and are all unique.
756 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
757 1 : std::vector<ScTypedStrData>::const_iterator it = aEntries.begin();
758 1 : CPPUNIT_ASSERT_EQUAL(OUString("Andy"), it->GetString());
759 1 : ++it;
760 1 : CPPUNIT_ASSERT_EQUAL(OUString("Bruce"), it->GetString());
761 1 : ++it;
762 1 : CPPUNIT_ASSERT_EQUAL(OUString("Charlie"), it->GetString());
763 1 : ++it;
764 1 : CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", it == aEntries.end());
765 :
766 1 : aEntries.clear();
767 1 : m_pDoc->GetDataEntries(0, MAXROW, 0, true, aEntries); // Try at the very bottom.
768 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
769 :
770 : // Make sure we get the same set of suggestions.
771 1 : it = aEntries.begin();
772 1 : CPPUNIT_ASSERT_EQUAL(OUString("Andy"), it->GetString());
773 1 : ++it;
774 1 : CPPUNIT_ASSERT_EQUAL(OUString("Bruce"), it->GetString());
775 1 : ++it;
776 1 : CPPUNIT_ASSERT_EQUAL(OUString("Charlie"), it->GetString());
777 1 : ++it;
778 1 : CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", it == aEntries.end());
779 :
780 1 : m_pDoc->DeleteTab(0);
781 1 : }
782 :
783 1 : void Test::testSelectionFunction()
784 : {
785 1 : m_pDoc->InsertTab(0, "Test");
786 :
787 : // Insert values into B2:B4.
788 1 : m_pDoc->SetString(ScAddress(1,1,0), "=1"); // formula
789 1 : m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
790 1 : m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
791 :
792 : // Insert strings into B5:B8.
793 1 : m_pDoc->SetString(ScAddress(1,4,0), "A");
794 1 : m_pDoc->SetString(ScAddress(1,5,0), "B");
795 1 : m_pDoc->SetString(ScAddress(1,6,0), "=\"C\""); // formula
796 1 : m_pDoc->SetString(ScAddress(1,7,0), "D");
797 :
798 : // Insert values into D2:D4.
799 1 : m_pDoc->SetValue(ScAddress(3,1,0), 4.0);
800 1 : m_pDoc->SetValue(ScAddress(3,2,0), 5.0);
801 1 : m_pDoc->SetValue(ScAddress(3,3,0), 6.0);
802 :
803 : // Insert edit text into D5.
804 1 : ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
805 1 : rEE.SetText("Rich Text");
806 1 : m_pDoc->SetEditText(ScAddress(3,4,0), rEE.CreateTextObject());
807 :
808 : // Insert Another string into D6.
809 1 : m_pDoc->SetString(ScAddress(3,5,0), "E");
810 :
811 : // Select B2:B8 & D2:D8 disjoint region.
812 1 : ScRangeList aRanges;
813 1 : aRanges.Append(ScRange(1,1,0,1,7,0)); // B2:B8
814 1 : aRanges.Append(ScRange(3,1,0,3,7,0)); // D2:D8
815 2 : ScMarkData aMark;
816 1 : aMark.MarkFromRangeList(aRanges, true);
817 :
818 : struct Check
819 : {
820 : ScSubTotalFunc meFunc;
821 : double mfExpected;
822 : };
823 :
824 : {
825 : Check aChecks[] =
826 : {
827 : { SUBTOTAL_FUNC_AVE, 3.5 },
828 : { SUBTOTAL_FUNC_CNT2, 12.0 },
829 : { SUBTOTAL_FUNC_CNT, 6.0 },
830 : { SUBTOTAL_FUNC_MAX, 6.0 },
831 : { SUBTOTAL_FUNC_MIN, 1.0 },
832 : { SUBTOTAL_FUNC_SUM, 21.0 },
833 : { SUBTOTAL_FUNC_SELECTION_COUNT, 14.0 }
834 1 : };
835 :
836 8 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
837 : {
838 7 : double fRes = 0.0;
839 7 : bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, ScAddress(), aMark, fRes);
840 7 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
841 7 : CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
842 : }
843 : }
844 :
845 : // Hide rows 4 and 6 and check the results again.
846 :
847 1 : m_pDoc->SetRowHidden(3, 3, 0, true);
848 1 : m_pDoc->SetRowHidden(5, 5, 0, true);
849 1 : CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(3, 0));
850 1 : CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(5, 0));
851 :
852 : {
853 : Check aChecks[] =
854 : {
855 : { SUBTOTAL_FUNC_AVE, 3.0 },
856 : { SUBTOTAL_FUNC_CNT2, 8.0 },
857 : { SUBTOTAL_FUNC_CNT, 4.0 },
858 : { SUBTOTAL_FUNC_MAX, 5.0 },
859 : { SUBTOTAL_FUNC_MIN, 1.0 },
860 : { SUBTOTAL_FUNC_SUM, 12.0 },
861 : { SUBTOTAL_FUNC_SELECTION_COUNT, 10.0 }
862 1 : };
863 :
864 8 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
865 : {
866 7 : double fRes = 0.0;
867 7 : bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, ScAddress(), aMark, fRes);
868 7 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
869 7 : CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
870 : }
871 : }
872 :
873 : // Make sure that when no selection is present, use the current cursor position.
874 2 : ScMarkData aEmpty;
875 :
876 : {
877 : // D3 (numeric cell containing 5.)
878 1 : ScAddress aPos(3, 2, 0);
879 :
880 : Check aChecks[] =
881 : {
882 : { SUBTOTAL_FUNC_AVE, 5.0 },
883 : { SUBTOTAL_FUNC_CNT2, 1.0 },
884 : { SUBTOTAL_FUNC_CNT, 1.0 },
885 : { SUBTOTAL_FUNC_MAX, 5.0 },
886 : { SUBTOTAL_FUNC_MIN, 5.0 },
887 : { SUBTOTAL_FUNC_SUM, 5.0 },
888 : { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
889 1 : };
890 :
891 8 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
892 : {
893 7 : double fRes = 0.0;
894 7 : bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, aPos, aEmpty, fRes);
895 7 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
896 7 : CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
897 : }
898 : }
899 :
900 : {
901 : // B7 (string formula cell containing ="C".)
902 1 : ScAddress aPos(1, 6, 0);
903 :
904 : Check aChecks[] =
905 : {
906 : { SUBTOTAL_FUNC_CNT2, 1.0 },
907 : { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
908 1 : };
909 :
910 3 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
911 : {
912 2 : double fRes = 0.0;
913 2 : bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, aPos, aEmpty, fRes);
914 2 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
915 2 : CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
916 : }
917 : }
918 :
919 2 : m_pDoc->DeleteTab(0);
920 1 : }
921 :
922 1 : void Test::testCopyToDocument()
923 : {
924 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "src"));
925 :
926 1 : m_pDoc->SetString(0, 0, 0, "Header");
927 1 : m_pDoc->SetString(0, 1, 0, "1");
928 1 : m_pDoc->SetString(0, 2, 0, "2");
929 1 : m_pDoc->SetString(0, 3, 0, "3");
930 1 : m_pDoc->SetString(0, 4, 0, "=4/2");
931 1 : m_pDoc->CalcAll();
932 :
933 : //note on A1
934 1 : ScAddress aAdrA1 (0, 0, 0); // numerical cell content
935 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
936 1 : pNote->SetText(aAdrA1, "Hello world in A1");
937 :
938 : // Copy statically to another document.
939 :
940 1 : ScDocument aDestDoc(SCDOCMODE_DOCUMENT);
941 1 : aDestDoc.InsertTab(0, "src");
942 1 : m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, &aDestDoc); // Copy A2:A4
943 1 : m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, &aDestDoc); // Copy A1
944 1 : m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, &aDestDoc); // Copy A5:A8
945 :
946 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), aDestDoc.GetString(0,0,0));
947 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), aDestDoc.GetString(0,1,0));
948 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), aDestDoc.GetString(0,2,0));
949 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), aDestDoc.GetString(0,3,0));
950 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), aDestDoc.GetString(0,4,0));
951 :
952 : // verify note
953 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note in A1 destDocument", aDestDoc.HasNote(ScAddress(0, 0, 0)));
954 2 : CPPUNIT_ASSERT_MESSAGE("The notes content should be the same on both documents",
955 1 : aDestDoc.GetNote(ScAddress(0, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText());
956 :
957 1 : m_pDoc->DeleteTab(0);
958 1 : }
959 :
960 : namespace {
961 :
962 : struct HoriIterCheck
963 : {
964 : SCCOL nCol;
965 : SCROW nRow;
966 : const char* pVal;
967 : };
968 :
969 : template<size_t _Size>
970 6 : bool checkHorizontalIterator(ScDocument* pDoc, const char* pData[][_Size], size_t nDataCount, const HoriIterCheck* pChecks, size_t nCheckCount)
971 : {
972 6 : ScAddress aPos(0,0,0);
973 6 : Test::insertRangeData(pDoc, aPos, pData, nDataCount);
974 6 : ScHorizontalCellIterator aIter(pDoc, 0, 0, 0, 1, nDataCount-1);
975 :
976 : SCCOL nCol;
977 : SCROW nRow;
978 6 : size_t i = 0;
979 31 : for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
980 : {
981 25 : if (i >= nCheckCount)
982 : {
983 0 : cerr << "hit invalid check " << i << " of " << nCheckCount << endl;
984 0 : CPPUNIT_FAIL("Iterator claims there is more data than there should be.");
985 0 : return false;
986 : }
987 :
988 25 : if (pChecks[i].nCol != nCol)
989 : {
990 0 : cerr << "Column mismatch " << pChecks[i].nCol << " vs. " << nCol << endl;
991 0 : return false;
992 : }
993 :
994 25 : if (pChecks[i].nRow != nRow)
995 : {
996 0 : cerr << "Row mismatch " << pChecks[i].nRow << " vs. " << nRow << endl;
997 0 : return false;
998 : }
999 :
1000 25 : if (OUString::createFromAscii(pChecks[i].pVal) != pCell->getString(pDoc))
1001 : {
1002 0 : cerr << "String mismatch " << pChecks[i].pVal << " vs. " <<
1003 : OUStringToOString(pCell->getString(pDoc), RTL_TEXTENCODING_UTF8).getStr() << endl;
1004 0 : return false;
1005 : }
1006 : }
1007 :
1008 6 : return true;
1009 : }
1010 :
1011 : }
1012 :
1013 1 : void Test::testHorizontalIterator()
1014 : {
1015 1 : m_pDoc->InsertTab(0, "test");
1016 :
1017 : {
1018 : // Raw data - mixed types
1019 : const char* aData[][2] = {
1020 : { "A", "B" },
1021 : { "C", "1" },
1022 : { "D", "2" },
1023 : { "E", "3" }
1024 1 : };
1025 :
1026 : HoriIterCheck aChecks[] = {
1027 : { 0, 0, "A" },
1028 : { 1, 0, "B" },
1029 : { 0, 1, "C" },
1030 : { 1, 1, "1" },
1031 : { 0, 2, "D" },
1032 : { 1, 2, "2" },
1033 : { 0, 3, "E" },
1034 : { 1, 3, "3" },
1035 1 : };
1036 :
1037 : bool bRes = checkHorizontalIterator(
1038 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1039 :
1040 1 : if (!bRes)
1041 0 : CPPUNIT_FAIL("Failed on test mixed.");
1042 : }
1043 :
1044 : {
1045 : // Raw data - 'hole' data
1046 : const char* aData[][2] = {
1047 : { "A", "B" },
1048 : { "C", 0 },
1049 : { "D", "E" },
1050 1 : };
1051 :
1052 : HoriIterCheck aChecks[] = {
1053 : { 0, 0, "A" },
1054 : { 1, 0, "B" },
1055 : { 0, 1, "C" },
1056 : { 0, 2, "D" },
1057 : { 1, 2, "E" },
1058 1 : };
1059 :
1060 : bool bRes = checkHorizontalIterator(
1061 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1062 :
1063 1 : if (!bRes)
1064 0 : CPPUNIT_FAIL("Failed on test hole.");
1065 : }
1066 :
1067 : {
1068 : // Very holy data
1069 : const char* aData[][2] = {
1070 : { 0, "A" },
1071 : { 0, 0 },
1072 : { 0, "1" },
1073 : { "B", 0 },
1074 : { "C", "2" },
1075 : { "D", "3" },
1076 : { "E", 0 },
1077 : { 0, "G" },
1078 : { 0, 0 },
1079 1 : };
1080 :
1081 : HoriIterCheck aChecks[] = {
1082 : { 1, 0, "A" },
1083 : { 1, 2, "1" },
1084 : { 0, 3, "B" },
1085 : { 0, 4, "C" },
1086 : { 1, 4, "2" },
1087 : { 0, 5, "D" },
1088 : { 1, 5, "3" },
1089 : { 0, 6, "E" },
1090 : { 1, 7, "G" },
1091 1 : };
1092 :
1093 : bool bRes = checkHorizontalIterator(
1094 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1095 :
1096 1 : if (!bRes)
1097 0 : CPPUNIT_FAIL("Failed on test holy.");
1098 : }
1099 :
1100 : {
1101 : // Degenerate case
1102 : const char* aData[][2] = {
1103 : { 0, 0 },
1104 : { 0, 0 },
1105 : { 0, 0 },
1106 1 : };
1107 :
1108 : bool bRes = checkHorizontalIterator(
1109 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), NULL, 0);
1110 :
1111 1 : if (!bRes)
1112 0 : CPPUNIT_FAIL("Failed on test degenerate.");
1113 : }
1114 :
1115 : {
1116 : // Data at end
1117 : const char* aData[][2] = {
1118 : { 0, 0 },
1119 : { 0, 0 },
1120 : { 0, "A" },
1121 1 : };
1122 :
1123 : HoriIterCheck aChecks[] = {
1124 : { 1, 2, "A" },
1125 1 : };
1126 :
1127 : bool bRes = checkHorizontalIterator(
1128 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1129 :
1130 1 : if (!bRes)
1131 0 : CPPUNIT_FAIL("Failed on test at end.");
1132 : }
1133 :
1134 : {
1135 : // Data in middle
1136 : const char* aData[][2] = {
1137 : { 0, 0 },
1138 : { 0, 0 },
1139 : { 0, "A" },
1140 : { 0, "1" },
1141 : { 0, 0 },
1142 1 : };
1143 :
1144 : HoriIterCheck aChecks[] = {
1145 : { 1, 2, "A" },
1146 : { 1, 3, "1" },
1147 1 : };
1148 :
1149 : bool bRes = checkHorizontalIterator(
1150 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1151 :
1152 1 : if (!bRes)
1153 0 : CPPUNIT_FAIL("Failed on test in middle.");
1154 : }
1155 :
1156 1 : m_pDoc->DeleteTab(0);
1157 1 : }
1158 :
1159 1 : void Test::testValueIterator()
1160 : {
1161 1 : m_pDoc->InsertTab(0, "Test");
1162 :
1163 : // Turn on "precision as shown" option.
1164 1 : ScDocOptions aOpt = m_pDoc->GetDocOptions();
1165 1 : aOpt.SetCalcAsShown(true);
1166 1 : m_pDoc->SetDocOptions(aOpt);
1167 :
1168 : // Purely horizontal data layout with numeric data.
1169 4 : for (SCCOL i = 1; i <= 3; ++i)
1170 3 : m_pDoc->SetValue(ScAddress(i,2,0), i);
1171 :
1172 : double fVal;
1173 : sal_uInt16 nErr;
1174 :
1175 : {
1176 1 : const double aChecks[] = { 1.0, 2.0, 3.0 };
1177 1 : size_t nCheckLen = SAL_N_ELEMENTS(aChecks);
1178 :
1179 1 : ScValueIterator aIter(m_pDoc, ScRange(1,2,0,3,2,0));
1180 1 : bool bHas = false;
1181 :
1182 1 : size_t nCheckPos = 0;
1183 4 : for (bHas = aIter.GetFirst(fVal, nErr); bHas; bHas = aIter.GetNext(fVal, nErr), ++nCheckPos)
1184 : {
1185 3 : CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1186 3 : CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos], fVal);
1187 3 : CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(0), nErr);
1188 : }
1189 : }
1190 :
1191 :
1192 1 : m_pDoc->DeleteTab(0);
1193 1 : }
1194 :
1195 1 : void Test::testFormulaDepTracking()
1196 : {
1197 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1198 :
1199 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1200 :
1201 : // B2 listens on D2.
1202 1 : m_pDoc->SetString(1, 1, 0, "=D2");
1203 1 : double val = -999.0; // dummy initial value
1204 1 : m_pDoc->GetValue(1, 1, 0, val);
1205 1 : CPPUNIT_ASSERT_MESSAGE("Referencing an empty cell should yield zero.", val == 0.0);
1206 :
1207 : // Changing the value of D2 should trigger recalculation of B2.
1208 1 : m_pDoc->SetValue(3, 1, 0, 1.1);
1209 1 : m_pDoc->GetValue(1, 1, 0, val);
1210 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 1.1);
1211 :
1212 : // And again.
1213 1 : m_pDoc->SetValue(3, 1, 0, 2.2);
1214 1 : m_pDoc->GetValue(1, 1, 0, val);
1215 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 2.2);
1216 :
1217 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1218 :
1219 : // Now, let's test the range dependency tracking.
1220 :
1221 : // B2 listens on D2:E6.
1222 1 : m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
1223 1 : m_pDoc->GetValue(1, 1, 0, val);
1224 1 : CPPUNIT_ASSERT_MESSAGE("Summing an empty range should yield zero.", val == 0.0);
1225 :
1226 : // Set value to E3. This should trigger recalc on B2.
1227 1 : m_pDoc->SetValue(4, 2, 0, 2.4);
1228 1 : m_pDoc->GetValue(1, 1, 0, val);
1229 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", val == 2.4);
1230 :
1231 : // Set value to D5 to trigger recalc again. Note that this causes an
1232 : // addition of 1.2 + 2.4 which is subject to binary floating point
1233 : // rounding error. We need to use approxEqual to assess its value.
1234 :
1235 1 : m_pDoc->SetValue(3, 4, 0, 1.2);
1236 1 : m_pDoc->GetValue(1, 1, 0, val);
1237 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
1238 :
1239 : // Change the value of D2 (boundary case).
1240 1 : m_pDoc->SetValue(3, 1, 0, 1.0);
1241 1 : m_pDoc->GetValue(1, 1, 0, val);
1242 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
1243 :
1244 : // Change the value of E6 (another boundary case).
1245 1 : m_pDoc->SetValue(4, 5, 0, 2.0);
1246 1 : m_pDoc->GetValue(1, 1, 0, val);
1247 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
1248 :
1249 : // Change the value of D6 (another boundary case).
1250 1 : m_pDoc->SetValue(3, 5, 0, 3.0);
1251 1 : m_pDoc->GetValue(1, 1, 0, val);
1252 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
1253 :
1254 : // Change the value of E2 (another boundary case).
1255 1 : m_pDoc->SetValue(4, 1, 0, 0.4);
1256 1 : m_pDoc->GetValue(1, 1, 0, val);
1257 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
1258 :
1259 : // Change the existing non-empty value cell (E2).
1260 1 : m_pDoc->SetValue(4, 1, 0, 2.4);
1261 1 : m_pDoc->GetValue(1, 1, 0, val);
1262 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
1263 :
1264 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1265 :
1266 : // Now, column-based dependency tracking. We now switch to the R1C1
1267 : // syntax which is easier to use for repeated relative references.
1268 :
1269 2 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1270 :
1271 1 : val = 0.0;
1272 10 : for (SCROW nRow = 1; nRow <= 9; ++nRow)
1273 : {
1274 : // Static value in column 1.
1275 9 : m_pDoc->SetValue(0, nRow, 0, ++val);
1276 :
1277 : // Formula in column 2 that references cell to the left.
1278 9 : m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
1279 :
1280 : // Formula in column 3 that references cell to the left.
1281 9 : m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
1282 : }
1283 :
1284 : // Check formula values.
1285 1 : val = 0.0;
1286 10 : for (SCROW nRow = 1; nRow <= 9; ++nRow)
1287 : {
1288 9 : ++val;
1289 9 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, nRow, 0) == val);
1290 9 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, nRow, 0) == val*2.0);
1291 : }
1292 :
1293 : // Intentionally insert a formula in column 1. This will break column 1's
1294 : // uniformity of consisting only of static value cells.
1295 1 : m_pDoc->SetString(0, 4, 0, "=R2C3");
1296 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(0, 4, 0) == 2.0);
1297 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, 4, 0) == 2.0);
1298 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, 4, 0) == 4.0);
1299 :
1300 2 : m_pDoc->DeleteTab(0);
1301 1 : }
1302 :
1303 1 : void Test::testFormulaDepTracking2()
1304 : {
1305 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1306 :
1307 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1308 :
1309 1 : double val = 2.0;
1310 1 : m_pDoc->SetValue(0, 0, 0, val);
1311 1 : val = 4.0;
1312 1 : m_pDoc->SetValue(1, 0, 0, val);
1313 1 : val = 5.0;
1314 1 : m_pDoc->SetValue(0, 1, 0, val);
1315 1 : m_pDoc->SetString(2, 0, 0, "=A1/B1");
1316 1 : m_pDoc->SetString(1, 1, 0, "=B1*C1");
1317 :
1318 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
1319 :
1320 1 : clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
1321 :
1322 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
1323 :
1324 1 : m_pDoc->DeleteTab(0);
1325 1 : }
1326 :
1327 1 : void Test::testFormulaMatrixResultUpdate()
1328 : {
1329 1 : m_pDoc->InsertTab(0, "Test");
1330 :
1331 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1332 :
1333 : // Set a numeric value to A1.
1334 1 : m_pDoc->SetValue(ScAddress(0,0,0), 11.0);
1335 :
1336 2 : ScMarkData aMark;
1337 1 : aMark.SelectOneTable(0);
1338 1 : m_pDoc->InsertMatrixFormula(1, 0, 1, 0, aMark, "=A1", NULL);
1339 1 : CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
1340 1 : ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
1341 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC);
1342 1 : pFC->SetChanged(false); // Clear this flag to simulate displaying of formula cell value on screen.
1343 :
1344 1 : m_pDoc->SetString(ScAddress(0,0,0), "ABC");
1345 1 : CPPUNIT_ASSERT_EQUAL(OUString("ABC"), m_pDoc->GetString(ScAddress(1,0,0)));
1346 1 : pFC->SetChanged(false);
1347 :
1348 : // Put a new value into A1. The formula should update.
1349 1 : m_pDoc->SetValue(ScAddress(0,0,0), 13.0);
1350 1 : CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(1,0,0)));
1351 :
1352 2 : m_pDoc->DeleteTab(0);
1353 1 : }
1354 :
1355 : namespace {
1356 :
1357 7 : bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
1358 : {
1359 7 : const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
1360 7 : if (pBC)
1361 : {
1362 0 : cerr << "Broadcaster shouldn't be here." << endl;
1363 0 : return false;
1364 : }
1365 :
1366 7 : pBC = rDoc.GetBroadcaster(rTo);
1367 7 : if (!pBC)
1368 : {
1369 0 : cerr << "Broadcaster should be here." << endl;
1370 0 : return false;
1371 : }
1372 7 : return true;
1373 : }
1374 :
1375 11 : ScToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
1376 : {
1377 11 : ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
1378 11 : if (!pFC)
1379 : {
1380 0 : cerr << "Formula cell expected, but not found." << endl;
1381 0 : return NULL;
1382 : }
1383 :
1384 11 : ScTokenArray* pTokens = pFC->GetCode();
1385 11 : if (!pTokens)
1386 : {
1387 0 : cerr << "Token array is not present." << endl;
1388 0 : return NULL;
1389 : }
1390 :
1391 11 : ScToken* pToken = static_cast<ScToken*>(pTokens->First());
1392 11 : if (!pToken || pToken->GetType() != formula::svSingleRef)
1393 : {
1394 0 : cerr << "Not a single reference token." << endl;
1395 0 : return NULL;
1396 : }
1397 :
1398 11 : return pToken;
1399 : }
1400 :
1401 9 : bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCsCOL nRelCol, SCsROW nRelRow)
1402 : {
1403 9 : ScToken* pToken = getSingleRefToken(rDoc, rPos);
1404 9 : if (!pToken)
1405 0 : return false;
1406 :
1407 9 : ScSingleRefData& rRef = pToken->GetSingleRef();
1408 9 : if (!rRef.IsColRel() || rRef.Col() != nRelCol)
1409 : {
1410 0 : cerr << "Unexpected relative column address." << endl;
1411 0 : return false;
1412 : }
1413 :
1414 9 : if (!rRef.IsRowRel() || rRef.Row() != nRelRow)
1415 : {
1416 0 : cerr << "Unexpected relative row address." << endl;
1417 0 : return false;
1418 : }
1419 :
1420 9 : return true;
1421 : }
1422 :
1423 2 : bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
1424 : {
1425 2 : ScToken* pToken = getSingleRefToken(rDoc, rPos);
1426 2 : if (!pToken)
1427 0 : return false;
1428 :
1429 2 : ScSingleRefData& rRef = pToken->GetSingleRef();
1430 2 : if (!rRef.IsDeleted())
1431 : {
1432 0 : cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
1433 0 : return false;
1434 : }
1435 :
1436 2 : return true;
1437 : }
1438 :
1439 : }
1440 :
1441 1 : void Test::testCellBroadcaster()
1442 : {
1443 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1444 :
1445 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1446 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
1447 1 : double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
1448 1 : CPPUNIT_ASSERT_EQUAL(0.0, val);
1449 :
1450 1 : const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1451 1 : CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
1452 :
1453 : // Change the value of A1 and make sure that B1 follows.
1454 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
1455 1 : val = m_pDoc->GetValue(ScAddress(1,0,0));
1456 1 : CPPUNIT_ASSERT_EQUAL(1.23, val);
1457 :
1458 : // Move column A down 5 cells. Make sure B1 now references A6, not A1.
1459 1 : m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
1460 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1461 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
1462 :
1463 : // Make sure the broadcaster has also moved.
1464 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1465 1 : broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
1466 :
1467 : // Set new value to A6 and make sure B1 gets updated.
1468 1 : m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
1469 1 : val = m_pDoc->GetValue(ScAddress(1,0,0));
1470 1 : CPPUNIT_ASSERT_EQUAL(45.6, val);
1471 :
1472 : // Move column A up 3 cells, and make sure B1 now references A3, not A6.
1473 1 : m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
1474 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1475 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
1476 :
1477 : // The broadcaster should also have been relocated from A6 to A3.
1478 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1479 1 : broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
1480 :
1481 : // Insert cells over A1:A10 and shift cells to right.
1482 1 : m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
1483 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1484 1 : checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
1485 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1486 1 : broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
1487 :
1488 : // Delete formula in C2, which should remove the broadcaster in B3.
1489 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1490 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
1491 1 : clearRange(m_pDoc, ScAddress(2,0,0));
1492 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
1493 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1494 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
1495 :
1496 : // Clear everything and start over.
1497 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1498 :
1499 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
1500 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1501 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
1502 :
1503 : // While column A is still empty, move column A down 2 cells. This should
1504 : // move the broadcaster from A1 to A3.
1505 1 : m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
1506 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1507 1 : broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
1508 :
1509 : // Move it back while column A is still empty.
1510 1 : m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
1511 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1512 1 : broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
1513 :
1514 : // Clear everything again
1515 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1516 :
1517 : // B1:B3 depends on A1:A3
1518 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1");
1519 1 : m_pDoc->SetString(ScAddress(1,1,0), "=A2");
1520 1 : m_pDoc->SetString(ScAddress(1,2,0), "=A3");
1521 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1522 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1523 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1524 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
1525 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
1526 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
1527 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1528 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
1529 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
1530 :
1531 : // Insert Rows at row 2, down 5 rows.
1532 1 : m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
1533 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1534 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1535 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1536 :
1537 : // Broadcasters in A2 and A3 should shift down by 5 rows.
1538 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1539 1 : broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
1540 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1541 1 : broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
1542 :
1543 : // B2 and B3 should reference shifted cells.
1544 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1545 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
1546 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1547 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
1548 :
1549 : // Delete cells with broadcasters.
1550 1 : m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
1551 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
1552 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
1553 :
1554 : // References in B2 and B3 should be invalid.
1555 2 : CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
1556 1 : checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
1557 2 : CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
1558 1 : checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
1559 :
1560 : // Clear everything again
1561 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1562 :
1563 : {
1564 : // Switch to R1C1 to make it easier to input relative references in multiple cells.
1565 1 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1566 :
1567 : // Have B1:B20 reference A1:A20.
1568 1 : val = 0.0;
1569 21 : for (SCROW i = 0; i < 20; ++i)
1570 : {
1571 20 : m_pDoc->SetValue(ScAddress(0,i,0), val++);
1572 20 : m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
1573 1 : }
1574 : }
1575 :
1576 : // Ensure that the formula cells show correct values, and the referenced
1577 : // cells have broadcasters.
1578 1 : val = 0.0;
1579 21 : for (SCROW i = 0; i < 20; ++i, ++val)
1580 : {
1581 20 : CPPUNIT_ASSERT_EQUAL(val, m_pDoc->GetValue(ScAddress(1,i,0)));
1582 20 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1583 20 : CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
1584 : }
1585 :
1586 : // Delete formula cells in B2:B19.
1587 1 : clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
1588 : // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
1589 1 : CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1590 1 : CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
1591 19 : for (SCROW i = 1; i <= 18; ++i)
1592 : {
1593 18 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1594 18 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
1595 : }
1596 :
1597 : // Clear everything again
1598 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1599 :
1600 1 : m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
1601 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1");
1602 1 : m_pDoc->SetString(ScAddress(2,0,0), "=B1");
1603 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,0,0));
1604 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,0,0));
1605 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(2,0,0));
1606 :
1607 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1608 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1609 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(1,0,0));
1610 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1611 :
1612 : // Change the value of A1 and make sure everyone follows suit.
1613 1 : m_pDoc->SetValue(ScAddress(0,0,0), 3.5);
1614 1 : CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(0,0,0));
1615 1 : CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(1,0,0));
1616 1 : CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(2,0,0));
1617 :
1618 : // Insert a column at column B.
1619 1 : m_pDoc->InsertCol(ScRange(1,0,0,1,MAXROW,0));
1620 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1621 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1622 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(2,0,0));
1623 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1624 :
1625 : // Change the value of A1 again.
1626 1 : m_pDoc->SetValue(ScAddress(0,0,0), 5.5);
1627 1 : CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(0,0,0));
1628 1 : CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(2,0,0));
1629 1 : CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(3,0,0));
1630 :
1631 1 : m_pDoc->DeleteTab(0);
1632 1 : }
1633 :
1634 1 : void Test::testFuncParam()
1635 : {
1636 :
1637 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1638 1 : m_pDoc->InsertTab (0, "foo"));
1639 :
1640 : // First, the normal case, with no missing parameters.
1641 1 : m_pDoc->SetString(0, 0, 0, "=AVERAGE(1;2;3)");
1642 1 : m_pDoc->CalcFormulaTree(false, false);
1643 : double val;
1644 1 : m_pDoc->GetValue(0, 0, 0, val);
1645 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 2);
1646 :
1647 : // Now function with missing parameters. Missing values should be treated
1648 : // as zeros.
1649 1 : m_pDoc->SetString(0, 0, 0, "=AVERAGE(1;;;)");
1650 1 : m_pDoc->CalcFormulaTree(false, false);
1651 1 : m_pDoc->GetValue(0, 0, 0, val);
1652 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 0.25);
1653 :
1654 : // Conversion of string to numeric argument.
1655 1 : m_pDoc->SetString(0, 0, 0, "=\"\"+3"); // empty string
1656 1 : m_pDoc->SetString(0, 1, 0, "=\" \"+3"); // only blank
1657 1 : m_pDoc->SetString(0, 2, 0, "=\" 4 \"+3"); // number in blanks
1658 1 : m_pDoc->SetString(0, 3, 0, "=\" x \"+3"); // non-numeric
1659 1 : m_pDoc->SetString(0, 4, 0, "=\"4.4\"+3"); // locale dependent
1660 :
1661 1 : OUString aVal;
1662 2 : ScCalcConfig aConfig;
1663 :
1664 : // With "Convert also locale dependent" and "Empty string as zero"=True option.
1665 1 : aConfig.meStringConversion = ScCalcConfig::STRING_CONVERSION_LOCALE_DEPENDENT;
1666 1 : aConfig.mbEmptyStringAsZero = true;
1667 1 : m_pDoc->SetCalcConfig(aConfig);
1668 1 : m_pDoc->CalcAll();
1669 1 : m_pDoc->GetValue(0, 0, 0, val);
1670 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1671 1 : m_pDoc->GetValue(0, 1, 0, val);
1672 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1673 1 : m_pDoc->GetValue(0, 2, 0, val);
1674 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
1675 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1676 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1677 1 : m_pDoc->GetValue(0, 4, 0, val);
1678 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7.4);
1679 :
1680 : // With "Convert also locale dependent" and "Empty string as zero"=False option.
1681 1 : aConfig.meStringConversion = ScCalcConfig::STRING_CONVERSION_LOCALE_DEPENDENT;
1682 1 : aConfig.mbEmptyStringAsZero = false;
1683 1 : m_pDoc->SetCalcConfig(aConfig);
1684 1 : m_pDoc->CalcAll();
1685 1 : aVal = m_pDoc->GetString( 0, 0, 0);
1686 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1687 1 : aVal = m_pDoc->GetString( 0, 1, 0);
1688 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1689 1 : m_pDoc->GetValue(0, 2, 0, val);
1690 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
1691 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1692 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1693 1 : m_pDoc->GetValue(0, 4, 0, val);
1694 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7.4);
1695 :
1696 : // With "Convert only unambiguous" and "Empty string as zero"=True option.
1697 1 : aConfig.meStringConversion = ScCalcConfig::STRING_CONVERSION_UNAMBIGUOUS;
1698 1 : aConfig.mbEmptyStringAsZero = true;
1699 1 : m_pDoc->SetCalcConfig(aConfig);
1700 1 : m_pDoc->CalcAll();
1701 1 : m_pDoc->GetValue(0, 0, 0, val);
1702 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1703 1 : m_pDoc->GetValue(0, 1, 0, val);
1704 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1705 1 : m_pDoc->GetValue(0, 2, 0, val);
1706 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
1707 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1708 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1709 1 : aVal = m_pDoc->GetString( 0, 4, 0);
1710 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1711 :
1712 : // With "Convert only unambiguous" and "Empty string as zero"=False option.
1713 1 : aConfig.meStringConversion = ScCalcConfig::STRING_CONVERSION_UNAMBIGUOUS;
1714 1 : aConfig.mbEmptyStringAsZero = false;
1715 1 : m_pDoc->SetCalcConfig(aConfig);
1716 1 : m_pDoc->CalcAll();
1717 1 : aVal = m_pDoc->GetString( 0, 0, 0);
1718 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1719 1 : aVal = m_pDoc->GetString( 0, 1, 0);
1720 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1721 1 : m_pDoc->GetValue(0, 2, 0, val);
1722 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
1723 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1724 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1725 1 : aVal = m_pDoc->GetString( 0, 4, 0);
1726 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1727 :
1728 : // With "Treat as zero" ("Empty string as zero" is ignored).
1729 1 : aConfig.meStringConversion = ScCalcConfig::STRING_CONVERSION_AS_ZERO;
1730 1 : aConfig.mbEmptyStringAsZero = true;
1731 1 : m_pDoc->SetCalcConfig(aConfig);
1732 1 : m_pDoc->CalcAll();
1733 1 : m_pDoc->GetValue(0, 0, 0, val);
1734 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1735 1 : m_pDoc->GetValue(0, 1, 0, val);
1736 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1737 1 : m_pDoc->GetValue(0, 2, 0, val);
1738 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1739 1 : m_pDoc->GetValue(0, 3, 0, val);
1740 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1741 1 : m_pDoc->GetValue(0, 4, 0, val);
1742 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1743 :
1744 : // With "Generate #VALUE! error" ("Empty string as zero" is ignored).
1745 1 : aConfig.meStringConversion = ScCalcConfig::STRING_CONVERSION_AS_ERROR;
1746 1 : aConfig.mbEmptyStringAsZero = false;
1747 1 : m_pDoc->SetCalcConfig(aConfig);
1748 1 : m_pDoc->CalcAll();
1749 1 : aVal = m_pDoc->GetString( 0, 0, 0);
1750 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1751 1 : aVal = m_pDoc->GetString( 0, 1, 0);
1752 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1753 1 : aVal = m_pDoc->GetString( 0, 2, 0);
1754 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1755 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1756 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1757 1 : aVal = m_pDoc->GetString( 0, 4, 0);
1758 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1759 :
1760 2 : m_pDoc->DeleteTab(0);
1761 1 : }
1762 :
1763 1 : void Test::testNamedRange()
1764 : {
1765 : RangeNameDef aNames[] = {
1766 : { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
1767 : { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
1768 : { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
1769 : { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
1770 1 : };
1771 :
1772 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "Sheet1"));
1773 :
1774 1 : m_pDoc->SetValue (0, 0, 0, 101);
1775 :
1776 1 : ScRangeName* pNames = new ScRangeName;
1777 1 : bool bSuccess = insertRangeNames(m_pDoc, pNames, aNames, aNames + SAL_N_ELEMENTS(aNames));
1778 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
1779 1 : m_pDoc->SetRangeName(pNames);
1780 :
1781 1 : ScRangeName* pNewRanges = m_pDoc->GetRangeName();
1782 1 : CPPUNIT_ASSERT(pNewRanges);
1783 :
1784 : // Make sure the index lookup does the right thing.
1785 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1786 : {
1787 4 : const ScRangeData* p = pNewRanges->findByIndex(aNames[i].mnIndex);
1788 4 : CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
1789 4 : OUString aName = p->GetName();
1790 4 : CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].mpName));
1791 4 : }
1792 :
1793 : // Test usage in formula expression.
1794 1 : m_pDoc->SetString (1, 0, 0, "=A1/Divisor");
1795 1 : m_pDoc->CalcAll();
1796 :
1797 : double result;
1798 1 : m_pDoc->GetValue (1, 0, 0, result);
1799 1 : CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 1.0);
1800 :
1801 : // Test copy-ability of range names.
1802 1 : ScRangeName* pCopiedRanges = new ScRangeName(*pNewRanges);
1803 1 : m_pDoc->SetRangeName(pCopiedRanges);
1804 : // Make sure the index lookup still works.
1805 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1806 : {
1807 4 : const ScRangeData* p = pCopiedRanges->findByIndex(aNames[i].mnIndex);
1808 4 : CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
1809 4 : OUString aName = p->GetName();
1810 4 : CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].mpName));
1811 4 : }
1812 :
1813 1 : m_pDoc->SetRangeName(NULL); // Delete the names.
1814 1 : m_pDoc->DeleteTab(0);
1815 1 : }
1816 :
1817 1 : void Test::testInsertNameList()
1818 : {
1819 1 : m_pDoc->InsertTab(0, "Test");
1820 :
1821 : RangeNameDef aNames[] = {
1822 : { "MyRange1", "$Test.$A$1:$A$100", 1 },
1823 : { "MyRange2", "$Test.$B$1:$B$100", 2 },
1824 : { "MyRange3", "$Test.$C$1:$C$100", 3 }
1825 1 : };
1826 :
1827 1 : ScRangeName* pNames = new ScRangeName;
1828 1 : bool bSuccess = insertRangeNames(m_pDoc, pNames, aNames, aNames + SAL_N_ELEMENTS(aNames));
1829 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
1830 1 : m_pDoc->SetRangeName(pNames);
1831 :
1832 1 : ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
1833 1 : ScAddress aPos(1,1,0);
1834 1 : rDocFunc.InsertNameList(aPos, true);
1835 :
1836 4 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i, aPos.IncRow())
1837 : {
1838 3 : OUString aName = m_pDoc->GetString(aPos);
1839 3 : CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aNames[i].mpName), aName);
1840 3 : ScAddress aExprPos = aPos;
1841 3 : aExprPos.IncCol();
1842 6 : OUString aExpr = m_pDoc->GetString(aExprPos);
1843 6 : OUString aExpected = "=";
1844 3 : aExpected += OUString::createFromAscii(aNames[i].mpExpr);
1845 3 : CPPUNIT_ASSERT_EQUAL(aExpected, aExpr);
1846 3 : }
1847 :
1848 1 : m_pDoc->DeleteTab(0);
1849 1 : }
1850 :
1851 1 : void Test::testCSV()
1852 : {
1853 1 : const int English = 0, European = 1;
1854 : struct {
1855 : const char *pStr; int eSep; bool bResult; double nValue;
1856 : } aTests[] = {
1857 : { "foo", English, false, 0.0 },
1858 : { "1.0", English, true, 1.0 },
1859 : { "1,0", English, false, 0.0 },
1860 : { "1.0", European, false, 0.0 },
1861 : { "1.000", European, true, 1000.0 },
1862 : { "1,000", European, true, 1.0 },
1863 : { "1.000", English, true, 1.0 },
1864 : { "1,000", English, true, 1000.0 },
1865 : { " 1.0", English, true, 1.0 },
1866 : { " 1.0 ", English, true, 1.0 },
1867 : { "1.0 ", European, false, 0.0 },
1868 : { "1.000", European, true, 1000.0 },
1869 : { "1137.999", English, true, 1137.999 },
1870 : { "1.000.00", European, false, 0.0 }
1871 1 : };
1872 15 : for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
1873 14 : OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
1874 14 : double nValue = 0.0;
1875 : bool bResult = ScStringUtil::parseSimpleNumber
1876 14 : (aStr, aTests[i].eSep == English ? '.' : ',',
1877 14 : aTests[i].eSep == English ? ',' : '.',
1878 28 : nValue);
1879 14 : CPPUNIT_ASSERT_MESSAGE ("CSV numeric detection failure", bResult == aTests[i].bResult);
1880 14 : CPPUNIT_ASSERT_MESSAGE ("CSV numeric value failure", nValue == aTests[i].nValue);
1881 14 : }
1882 1 : }
1883 :
1884 : template<typename Evaluator>
1885 4 : void checkMatrixElements(const ScMatrix& rMat)
1886 : {
1887 : SCSIZE nC, nR;
1888 4 : rMat.GetDimensions(nC, nR);
1889 : Evaluator aEval;
1890 32 : for (SCSIZE i = 0; i < nC; ++i)
1891 : {
1892 508 : for (SCSIZE j = 0; j < nR; ++j)
1893 : {
1894 480 : aEval(i, j, rMat.Get(i, j));
1895 : }
1896 : }
1897 4 : }
1898 :
1899 : struct AllZeroMatrix
1900 : {
1901 40 : void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
1902 : {
1903 40 : CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
1904 40 : CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
1905 40 : }
1906 : };
1907 :
1908 : struct PartiallyFilledZeroMatrix
1909 : {
1910 40 : void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
1911 : {
1912 40 : CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
1913 40 : if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
1914 : {
1915 14 : CPPUNIT_ASSERT_MESSAGE("element value must be 3.0", rVal.fVal == 3.0);
1916 : }
1917 : else
1918 : {
1919 26 : CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
1920 : }
1921 40 : }
1922 : };
1923 :
1924 : struct AllEmptyMatrix
1925 : {
1926 200 : void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
1927 : {
1928 200 : CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
1929 200 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
1930 200 : }
1931 : };
1932 :
1933 : struct PartiallyFilledEmptyMatrix
1934 : {
1935 200 : void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
1936 : {
1937 200 : if (nCol == 1 && nRow == 1)
1938 : {
1939 1 : CPPUNIT_ASSERT_MESSAGE("element is not of boolean type", rVal.nType == SC_MATVAL_BOOLEAN);
1940 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == 1.0);
1941 : }
1942 199 : else if (nCol == 4 && nRow == 5)
1943 : {
1944 1 : CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_VALUE);
1945 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == -12.5);
1946 : }
1947 198 : else if (nCol == 8 && nRow == 2)
1948 : {
1949 1 : CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING);
1950 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr.getString() == "Test");
1951 : }
1952 197 : else if (nCol == 8 && nRow == 11)
1953 : {
1954 1 : CPPUNIT_ASSERT_MESSAGE("element is not of empty path type", rVal.nType == SC_MATVAL_EMPTYPATH);
1955 1 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
1956 : }
1957 : else
1958 : {
1959 196 : CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
1960 196 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
1961 : }
1962 200 : }
1963 : };
1964 :
1965 1 : void Test::testMatrix()
1966 : {
1967 1 : svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
1968 2 : ScMatrixRef pMat, pMat2;
1969 :
1970 : // First, test the zero matrix type.
1971 1 : pMat = new ScMatrix(0, 0, 0.0);
1972 : SCSIZE nC, nR;
1973 1 : pMat->GetDimensions(nC, nR);
1974 1 : CPPUNIT_ASSERT_MESSAGE("matrix is not empty", nC == 0 && nR == 0);
1975 1 : pMat->Resize(4, 10, 0.0);
1976 1 : pMat->GetDimensions(nC, nR);
1977 1 : CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 4 && nR == 10);
1978 2 : CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
1979 1 : !pMat->And() && !pMat->Or());
1980 :
1981 : // Resizing into a larger matrix should fill the void space with zeros.
1982 1 : checkMatrixElements<AllZeroMatrix>(*pMat);
1983 :
1984 1 : pMat->FillDouble(3.0, 1, 2, 2, 8);
1985 1 : checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
1986 1 : CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
1987 2 : CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
1988 1 : !pMat->And() && pMat->Or());
1989 1 : pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
1990 2 : CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
1991 1 : pMat->And() && pMat->Or());
1992 :
1993 : // Test the AND and OR evaluations.
1994 1 : pMat = new ScMatrix(2, 2, 0.0);
1995 :
1996 : // Only some of the elements are non-zero.
1997 1 : pMat->PutBoolean(true, 0, 0);
1998 1 : pMat->PutDouble(1.0, 1, 1);
1999 1 : CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2000 1 : CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
2001 :
2002 : // All of the elements are non-zero.
2003 1 : pMat->PutBoolean(true, 0, 1);
2004 1 : pMat->PutDouble(2.3, 1, 0);
2005 1 : CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2006 1 : CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
2007 :
2008 : // Now test the emtpy matrix type.
2009 1 : pMat = new ScMatrix(10, 20);
2010 1 : pMat->GetDimensions(nC, nR);
2011 1 : CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 10 && nR == 20);
2012 1 : checkMatrixElements<AllEmptyMatrix>(*pMat);
2013 :
2014 1 : pMat->PutBoolean(true, 1, 1);
2015 1 : pMat->PutDouble(-12.5, 4, 5);
2016 1 : pMat->PutString(rPool.intern("Test"), 8, 2);
2017 1 : pMat->PutEmptyPath(8, 11);
2018 1 : checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
2019 :
2020 : // Test resizing.
2021 1 : pMat = new ScMatrix(0, 0);
2022 1 : pMat->Resize(2, 2, 1.5);
2023 1 : pMat->PutEmpty(1, 1);
2024 :
2025 1 : CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
2026 1 : CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
2027 1 : CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
2028 1 : CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
2029 :
2030 : // Max and min values.
2031 1 : pMat = new ScMatrix(2, 2, 0.0);
2032 1 : pMat->PutDouble(-10, 0, 0);
2033 1 : pMat->PutDouble(-12, 0, 1);
2034 1 : pMat->PutDouble(-8, 1, 0);
2035 1 : pMat->PutDouble(-25, 1, 1);
2036 1 : CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
2037 1 : CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
2038 1 : pMat->PutString(rPool.intern("Test"), 0, 0);
2039 1 : CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
2040 1 : CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
2041 1 : pMat->PutBoolean(true, 0, 0);
2042 1 : CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
2043 1 : pMat = new ScMatrix(2, 2, 10.0);
2044 1 : pMat->PutBoolean(false, 0, 0);
2045 1 : pMat->PutDouble(12.5, 1, 1);
2046 1 : CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
2047 1 : CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
2048 :
2049 : // Convert matrix into a linear double array. String elements become NaN
2050 : // and empty elements become 0.
2051 1 : pMat = new ScMatrix(3, 3);
2052 1 : pMat->PutDouble(2.5, 0, 0);
2053 1 : pMat->PutDouble(1.2, 0, 1);
2054 1 : pMat->PutString(rPool.intern("A"), 1, 1);
2055 1 : pMat->PutDouble(2.3, 2, 1);
2056 1 : pMat->PutDouble(-20, 2, 2);
2057 :
2058 : double fNaN;
2059 1 : rtl::math::setNan(&fNaN);
2060 :
2061 2 : std::vector<double> aDoubles;
2062 1 : pMat->GetDoubleArray(aDoubles);
2063 :
2064 : {
2065 1 : const double pChecks[] = { 2.5, 1.2, 0, 0, fNaN, 0, 0, 2.3, -20 };
2066 1 : CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2067 10 : for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2068 : {
2069 9 : if (rtl::math::isNan(pChecks[i]))
2070 1 : CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", rtl::math::isNan(aDoubles[i]));
2071 : else
2072 8 : CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2073 : }
2074 : }
2075 :
2076 1 : pMat2 = new ScMatrix(3, 3, 10.0);
2077 1 : pMat2->PutString(rPool.intern("B"), 1, 0);
2078 1 : pMat2->MergeDoubleArray(aDoubles, ScMatrix::Mul);
2079 :
2080 : {
2081 1 : const double pChecks[] = { 25, 12, 0, fNaN, fNaN, 0, 0, 23, -200 };
2082 1 : CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2083 10 : for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2084 : {
2085 9 : if (rtl::math::isNan(pChecks[i]))
2086 2 : CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", rtl::math::isNan(aDoubles[i]));
2087 : else
2088 7 : CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2089 : }
2090 1 : }
2091 1 : }
2092 :
2093 1 : void Test::testEnterMixedMatrix()
2094 : {
2095 1 : m_pDoc->InsertTab(0, "foo");
2096 :
2097 : // Insert the source values in A1:B2.
2098 1 : m_pDoc->SetString(0, 0, 0, "A");
2099 1 : m_pDoc->SetString(1, 0, 0, "B");
2100 1 : double val = 1.0;
2101 1 : m_pDoc->SetValue(0, 1, 0, val);
2102 1 : val = 2.0;
2103 1 : m_pDoc->SetValue(1, 1, 0, val);
2104 :
2105 : // Create a matrix range in A4:B5 referencing A1:B2.
2106 1 : ScMarkData aMark;
2107 1 : aMark.SelectOneTable(0);
2108 1 : m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, "=A1:B2", NULL);
2109 :
2110 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
2111 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
2112 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
2113 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
2114 :
2115 1 : m_pDoc->DeleteTab(0);
2116 1 : }
2117 :
2118 1 : void Test::testCellCopy()
2119 : {
2120 1 : m_pDoc->InsertTab(0, "TestTab");
2121 1 : ScAddress aSrc(0,0,0);
2122 1 : ScAddress aDest(0,1,0);
2123 1 : OUString aStr("please copy me");
2124 1 : m_pDoc->SetString(aSrc, "please copy me");
2125 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aSrc), aStr);
2126 : // copy to self - why not ?
2127 1 : m_pDoc->CopyCellToDocument(aSrc,aDest,*m_pDoc);
2128 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aDest), aStr);
2129 1 : }
2130 :
2131 1 : void Test::testSheetCopy()
2132 : {
2133 1 : m_pDoc->InsertTab(0, "TestTab");
2134 1 : CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
2135 :
2136 : // Insert text in A1.
2137 1 : m_pDoc->SetString(ScAddress(0,0,0), "copy me");
2138 :
2139 : // Insert edit cells in B1:B3.
2140 1 : ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
2141 1 : rEE.SetText("Edit 1");
2142 1 : m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject());
2143 1 : rEE.SetText("Edit 2");
2144 1 : m_pDoc->SetEditText(ScAddress(1,1,0), rEE.CreateTextObject());
2145 1 : rEE.SetText("Edit 3");
2146 1 : m_pDoc->SetEditText(ScAddress(1,2,0), rEE.CreateTextObject());
2147 :
2148 : SCROW nRow1, nRow2;
2149 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2150 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
2151 :
2152 : // insert a note
2153 1 : ScAddress aAdrA1 (0,2,0); // empty cell content.
2154 1 : ScPostIt *pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
2155 1 : pNoteA1->SetText(aAdrA1, "Hello world in A3");
2156 :
2157 : // Copy and test the result.
2158 1 : m_pDoc->CopyTab(0, 1);
2159 1 : CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
2160 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2161 1 : CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
2162 1 : CPPUNIT_ASSERT_MESSAGE("There should be note on A3 in new sheet", m_pDoc->HasNote(ScAddress(0,2,1)));
2163 1 : CPPUNIT_ASSERT_EQUAL(OUString("copy me"), m_pDoc->GetString(ScAddress(0,0,1)));
2164 :
2165 : // Check the copied edit cells.
2166 1 : const EditTextObject* pEditObj = m_pDoc->GetEditText(ScAddress(1,0,1));
2167 1 : CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B1.", pEditObj);
2168 1 : CPPUNIT_ASSERT_EQUAL(OUString("Edit 1"), pEditObj->GetText(0));
2169 1 : pEditObj = m_pDoc->GetEditText(ScAddress(1,1,1));
2170 1 : CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B2.", pEditObj);
2171 1 : CPPUNIT_ASSERT_EQUAL(OUString("Edit 2"), pEditObj->GetText(0));
2172 1 : pEditObj = m_pDoc->GetEditText(ScAddress(1,2,1));
2173 1 : CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B3.", pEditObj);
2174 1 : CPPUNIT_ASSERT_EQUAL(OUString("Edit 3"), pEditObj->GetText(0));
2175 :
2176 1 : m_pDoc->DeleteTab(1);
2177 :
2178 1 : m_pDoc->SetRowHidden(5, 10, 0, true);
2179 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2180 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
2181 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2182 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
2183 1 : bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2184 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
2185 :
2186 : // Copy the sheet once again.
2187 1 : m_pDoc->CopyTab(0, 1);
2188 1 : CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
2189 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2190 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
2191 1 : bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2192 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
2193 1 : bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2194 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
2195 1 : m_pDoc->DeleteTab(1);
2196 1 : m_pDoc->DeleteTab(0);
2197 1 : }
2198 :
2199 1 : void Test::testSheetMove()
2200 : {
2201 1 : m_pDoc->InsertTab(0, "TestTab1");
2202 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount(), static_cast<SCTAB>(1));
2203 : SCROW nRow1, nRow2;
2204 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2205 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
2206 :
2207 : //test if inserting before another sheet works
2208 1 : m_pDoc->InsertTab(0, "TestTab2");
2209 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
2210 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2211 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
2212 :
2213 : // Move and test the result.
2214 1 : m_pDoc->MoveTab(0, 1);
2215 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
2216 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2217 1 : CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
2218 1 : OUString aName;
2219 1 : m_pDoc->GetName(0, aName);
2220 1 : CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab1" );
2221 :
2222 1 : m_pDoc->SetRowHidden(5, 10, 0, true);
2223 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2224 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
2225 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2226 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
2227 1 : bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2228 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
2229 :
2230 : // Move the sheet once again.
2231 1 : m_pDoc->MoveTab(1, 0);
2232 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
2233 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2234 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
2235 1 : bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2236 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
2237 1 : bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2238 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
2239 1 : m_pDoc->GetName(0, aName);
2240 1 : CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab2" );
2241 1 : m_pDoc->DeleteTab(1);
2242 1 : m_pDoc->DeleteTab(0);
2243 1 : }
2244 :
2245 1 : void Test::testDataArea()
2246 : {
2247 1 : m_pDoc->InsertTab(0, "Data");
2248 :
2249 : // Totally empty sheet should be rightfully considered empty in all accounts.
2250 1 : CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
2251 1 : CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
2252 :
2253 : // Now, set borders in some cells....
2254 1 : ::editeng::SvxBorderLine aLine(NULL, 50, table::BorderLineStyle::SOLID);
2255 1 : SvxBoxItem aBorderItem(ATTR_BORDER);
2256 1 : aBorderItem.SetLine(&aLine, BOX_LINE_LEFT);
2257 1 : aBorderItem.SetLine(&aLine, BOX_LINE_RIGHT);
2258 101 : for (SCROW i = 0; i < 100; ++i)
2259 : // Set borders from row 1 to 100.
2260 100 : m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
2261 :
2262 : // Now the sheet is considered non-empty for printing purposes, but still
2263 : // be empty in all the other cases.
2264 2 : CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
2265 1 : !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
2266 2 : CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
2267 1 : m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
2268 :
2269 : // Adding a real cell content should turn the block non-empty.
2270 1 : m_pDoc->SetString(0, 0, 0, "Some text");
2271 2 : CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
2272 1 : !m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
2273 :
2274 : // TODO: Add more tests for normal data area calculation.
2275 :
2276 1 : m_pDoc->DeleteTab(0);
2277 1 : }
2278 :
2279 1 : void Test::testStreamValid()
2280 : {
2281 1 : m_pDoc->InsertTab(0, "Sheet1");
2282 1 : m_pDoc->InsertTab(1, "Sheet2");
2283 1 : m_pDoc->InsertTab(2, "Sheet3");
2284 1 : m_pDoc->InsertTab(3, "Sheet4");
2285 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", m_pDoc->GetTableCount(), static_cast<SCTAB>(4));
2286 :
2287 1 : OUString a1("A1");
2288 2 : OUString a2("A2");
2289 2 : OUString test;
2290 :
2291 : // Put values into Sheet1.
2292 1 : m_pDoc->SetString(0, 0, 0, a1);
2293 1 : m_pDoc->SetString(0, 1, 0, a2);
2294 1 : test = m_pDoc->GetString(0, 0, 0);
2295 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A1", test.equals(a1));
2296 1 : test = m_pDoc->GetString(0, 1, 0);
2297 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A2", test.equals(a2));
2298 :
2299 : // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
2300 1 : m_pDoc->SetString(0, 0, 1, "=Sheet1.A1");
2301 1 : m_pDoc->SetString(0, 1, 1, "=Sheet1.A2");
2302 1 : m_pDoc->SetString(0, 0, 2, "=Sheet1.A1");
2303 1 : m_pDoc->SetString(0, 0, 3, "=Sheet1.A2");
2304 :
2305 1 : test = m_pDoc->GetString(0, 0, 1);
2306 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A1", test.equals(a1));
2307 1 : test = m_pDoc->GetString(0, 1, 1);
2308 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A2", test.equals(a2));
2309 1 : test = m_pDoc->GetString(0, 0, 2);
2310 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a1));
2311 1 : test = m_pDoc->GetString(0, 0, 3);
2312 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a2));
2313 :
2314 : // Set all sheet streams valid after all the initial cell values are in
2315 : // place. In reality we need to have real XML streams stored in order to
2316 : // claim they are valid, but we are just testing the flag values here.
2317 1 : m_pDoc->SetStreamValid(0, true);
2318 1 : m_pDoc->SetStreamValid(1, true);
2319 1 : m_pDoc->SetStreamValid(2, true);
2320 1 : m_pDoc->SetStreamValid(3, true);
2321 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
2322 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
2323 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
2324 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
2325 :
2326 : // Now, insert a new row at row 2 position on Sheet1. This will move cell
2327 : // A2 downward but cell A1 remains unmoved.
2328 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 2);
2329 1 : test = m_pDoc->GetString(0, 0, 0);
2330 1 : CPPUNIT_ASSERT_MESSAGE("Cell A1 should not have moved.", test.equals(a1));
2331 1 : test = m_pDoc->GetString(0, 3, 0);
2332 1 : CPPUNIT_ASSERT_MESSAGE("the old cell A2 should now be at A4.", test.equals(a2));
2333 2 : ScRefCellValue aCell;
2334 1 : aCell.assign(*m_pDoc, ScAddress(0,1,0));
2335 1 : CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
2336 1 : aCell.assign(*m_pDoc, ScAddress(0,2,0));
2337 1 : CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
2338 :
2339 : // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
2340 : // invalidated, whereas Sheet3's stream should still be valid.
2341 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
2342 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
2343 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
2344 1 : CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
2345 :
2346 1 : m_pDoc->DeleteTab(3);
2347 1 : m_pDoc->DeleteTab(2);
2348 1 : m_pDoc->DeleteTab(1);
2349 2 : m_pDoc->DeleteTab(0);
2350 1 : }
2351 :
2352 1 : void Test::testFunctionLists()
2353 : {
2354 : const char* aDataBase[] = {
2355 : "DAVERAGE",
2356 : "DCOUNT",
2357 : "DCOUNTA",
2358 : "DGET",
2359 : "DMAX",
2360 : "DMIN",
2361 : "DPRODUCT",
2362 : "DSTDEV",
2363 : "DSTDEVP",
2364 : "DSUM",
2365 : "DVAR",
2366 : "DVARP",
2367 : 0
2368 1 : };
2369 :
2370 : const char* aDateTime[] = {
2371 : "DATE",
2372 : "DATEDIF",
2373 : "DATEVALUE",
2374 : "DAY",
2375 : "DAYS",
2376 : "DAYS360",
2377 : "EASTERSUNDAY",
2378 : "HOUR",
2379 : "MINUTE",
2380 : "MONTH",
2381 : "NETWORKDAYS.INTL",
2382 : "NOW",
2383 : "SECOND",
2384 : "TIME",
2385 : "TIMEVALUE",
2386 : "TODAY",
2387 : "WEEKDAY",
2388 : "WEEKNUM",
2389 : "WORKDAY.INTL",
2390 : "YEAR",
2391 : 0
2392 1 : };
2393 :
2394 : const char* aFinancial[] = {
2395 : "CUMIPMT",
2396 : "CUMPRINC",
2397 : "DB",
2398 : "DDB",
2399 : "DURATION",
2400 : "EFFECTIVE",
2401 : "FV",
2402 : "IPMT",
2403 : "IRR",
2404 : "ISPMT",
2405 : "MIRR",
2406 : "NOMINAL",
2407 : "NPER",
2408 : "NPV",
2409 : "PMT",
2410 : "PPMT",
2411 : "PV",
2412 : "RATE",
2413 : "RRI",
2414 : "SLN",
2415 : "SYD",
2416 : "VDB",
2417 : 0
2418 1 : };
2419 :
2420 : const char* aInformation[] = {
2421 : "CELL",
2422 : "CURRENT",
2423 : "FORMULA",
2424 : "INFO",
2425 : "ISBLANK",
2426 : "ISERR",
2427 : "ISERROR",
2428 : "ISFORMULA",
2429 : "ISLOGICAL",
2430 : "ISNA",
2431 : "ISNONTEXT",
2432 : "ISNUMBER",
2433 : "ISREF",
2434 : "ISTEXT",
2435 : "N",
2436 : "NA",
2437 : "TYPE",
2438 : 0
2439 1 : };
2440 :
2441 : const char* aLogical[] = {
2442 : "AND",
2443 : "FALSE",
2444 : "IF",
2445 : "IFERROR",
2446 : "IFNA",
2447 : "NOT",
2448 : "OR",
2449 : "TRUE",
2450 : "XOR",
2451 : 0
2452 1 : };
2453 :
2454 : const char* aMathematical[] = {
2455 : "ABS",
2456 : "ACOS",
2457 : "ACOSH",
2458 : "ACOT",
2459 : "ACOTH",
2460 : "ASIN",
2461 : "ASINH",
2462 : "ATAN",
2463 : "ATAN2",
2464 : "ATANH",
2465 : "BITAND",
2466 : "BITLSHIFT",
2467 : "BITOR",
2468 : "BITRSHIFT",
2469 : "BITXOR",
2470 : "CEILING",
2471 : "CEILING.PRECISE",
2472 : "COMBIN",
2473 : "COMBINA",
2474 : "CONVERT",
2475 : "COS",
2476 : "COSH",
2477 : "COT",
2478 : "COTH",
2479 : "CSC",
2480 : "CSCH",
2481 : "DEGREES",
2482 : "EUROCONVERT",
2483 : "EVEN",
2484 : "EXP",
2485 : "FACT",
2486 : "FLOOR",
2487 : "FLOOR.PRECISE",
2488 : "GCD",
2489 : "INT",
2490 : "ISEVEN",
2491 : "ISO.CEILING",
2492 : "ISODD",
2493 : "LCM",
2494 : "LN",
2495 : "LOG",
2496 : "LOG10",
2497 : "MOD",
2498 : "ODD",
2499 : "PI",
2500 : "POWER",
2501 : "PRODUCT",
2502 : "RADIANS",
2503 : "RAND",
2504 : "ROUND",
2505 : "ROUNDDOWN",
2506 : "ROUNDUP",
2507 : "SEC",
2508 : "SECH",
2509 : "SIGN",
2510 : "SIN",
2511 : "SINH",
2512 : "SQRT",
2513 : "SUBTOTAL",
2514 : "SUM",
2515 : "SUMIF",
2516 : "SUMIFS",
2517 : "SUMSQ",
2518 : "TAN",
2519 : "TANH",
2520 : "TRUNC",
2521 : 0
2522 1 : };
2523 :
2524 : const char* aArray[] = {
2525 : "FREQUENCY",
2526 : "GROWTH",
2527 : "LINEST",
2528 : "LOGEST",
2529 : "MDETERM",
2530 : "MINVERSE",
2531 : "MMULT",
2532 : "MUNIT",
2533 : "SUMPRODUCT",
2534 : "SUMX2MY2",
2535 : "SUMX2PY2",
2536 : "SUMXMY2",
2537 : "TRANSPOSE",
2538 : "TREND",
2539 : 0
2540 1 : };
2541 :
2542 : const char* aStatistical[] = {
2543 : "AVEDEV",
2544 : "AVERAGE",
2545 : "AVERAGEA",
2546 : "AVERAGEIF",
2547 : "AVERAGEIFS",
2548 : "B",
2549 : "BETA.DIST",
2550 : "BETA.INV",
2551 : "BETADIST",
2552 : "BETAINV",
2553 : "BINOM.DIST",
2554 : "BINOM.INV",
2555 : "BINOMDIST",
2556 : "CHIDIST",
2557 : "CHIINV",
2558 : "CHISQ.DIST",
2559 : "CHISQ.DIST.RT",
2560 : "CHISQ.INV",
2561 : "CHISQ.INV.RT",
2562 : "CHISQ.TEST",
2563 : "CHISQDIST",
2564 : "CHISQINV",
2565 : "CHITEST",
2566 : "CONFIDENCE",
2567 : "CONFIDENCE.NORM",
2568 : "CONFIDENCE.T",
2569 : "CORREL",
2570 : "COUNT",
2571 : "COUNTA",
2572 : "COUNTBLANK",
2573 : "COUNTIF",
2574 : "COUNTIFS",
2575 : "COVAR",
2576 : "COVARIANCE.P",
2577 : "COVARIANCE.S",
2578 : "CRITBINOM",
2579 : "DEVSQ",
2580 : "EXPON.DIST",
2581 : "EXPONDIST",
2582 : "F.DIST",
2583 : "F.DIST.RT",
2584 : "F.INV",
2585 : "F.INV.RT",
2586 : "F.TEST",
2587 : "FDIST",
2588 : "FINV",
2589 : "FISHER",
2590 : "FISHERINV",
2591 : "FORECAST",
2592 : "FTEST",
2593 : "GAMMA",
2594 : "GAMMA.DIST",
2595 : "GAMMA.INV",
2596 : "GAMMADIST",
2597 : "GAMMAINV",
2598 : "GAMMALN",
2599 : "GAMMALN.PRECISE",
2600 : "GAUSS",
2601 : "GEOMEAN",
2602 : "HARMEAN",
2603 : "HYPGEOM.DIST",
2604 : "HYPGEOMDIST",
2605 : "INTERCEPT",
2606 : "KURT",
2607 : "LARGE",
2608 : "LOGINV",
2609 : "LOGNORM.DIST",
2610 : "LOGNORM.INV",
2611 : "LOGNORMDIST",
2612 : "MAX",
2613 : "MAXA",
2614 : "MEDIAN",
2615 : "MIN",
2616 : "MINA",
2617 : "MODE",
2618 : "MODE.MULT",
2619 : "MODE.SNGL",
2620 : "NEGBINOM.DIST",
2621 : "NEGBINOMDIST",
2622 : "NORM.DIST",
2623 : "NORM.INV",
2624 : "NORM.S.DIST",
2625 : "NORM.S.INV",
2626 : "NORMDIST",
2627 : "NORMINV",
2628 : "NORMSDIST",
2629 : "NORMSINV",
2630 : "PEARSON",
2631 : "PERCENTILE",
2632 : "PERCENTILE.EXC",
2633 : "PERCENTILE.INC",
2634 : "PERCENTRANK",
2635 : "PERCENTRANK.EXC",
2636 : "PERCENTRANK.INC",
2637 : "PERMUT",
2638 : "PERMUTATIONA",
2639 : "PHI",
2640 : "POISSON",
2641 : "POISSON.DIST",
2642 : "PROB",
2643 : "QUARTILE",
2644 : "QUARTILE.EXC",
2645 : "QUARTILE.INC",
2646 : "RANK",
2647 : "RANK.AVG",
2648 : "RANK.EQ",
2649 : "RSQ",
2650 : "SKEW",
2651 : "SKEWP",
2652 : "SLOPE",
2653 : "SMALL",
2654 : "STANDARDIZE",
2655 : "STDEV",
2656 : "STDEV.P",
2657 : "STDEV.S",
2658 : "STDEVA",
2659 : "STDEVP",
2660 : "STDEVPA",
2661 : "STEYX",
2662 : "T.DIST",
2663 : "T.DIST.2T",
2664 : "T.DIST.RT",
2665 : "T.INV",
2666 : "T.INV.2T",
2667 : "T.TEST",
2668 : "TDIST",
2669 : "TINV",
2670 : "TRIMMEAN",
2671 : "TTEST",
2672 : "VAR",
2673 : "VAR.P",
2674 : "VAR.S",
2675 : "VARA",
2676 : "VARP",
2677 : "VARPA",
2678 : "WEIBULL",
2679 : "WEIBULL.DIST",
2680 : "Z.TEST",
2681 : "ZTEST",
2682 : 0
2683 1 : };
2684 :
2685 : const char* aSpreadsheet[] = {
2686 : "ADDRESS",
2687 : "AREAS",
2688 : "CHOOSE",
2689 : "COLUMN",
2690 : "COLUMNS",
2691 : "DDE",
2692 : "ERRORTYPE",
2693 : "GETPIVOTDATA",
2694 : "HLOOKUP",
2695 : "HYPERLINK",
2696 : "INDEX",
2697 : "INDIRECT",
2698 : "LOOKUP",
2699 : "MATCH",
2700 : "OFFSET",
2701 : "ROW",
2702 : "ROWS",
2703 : "SHEET",
2704 : "SHEETS",
2705 : "STYLE",
2706 : "VLOOKUP",
2707 : 0
2708 1 : };
2709 :
2710 : const char* aText[] = {
2711 : "ARABIC",
2712 : "ASC",
2713 : "BAHTTEXT",
2714 : "BASE",
2715 : "CHAR",
2716 : "CLEAN",
2717 : "CODE",
2718 : "CONCATENATE",
2719 : "DECIMAL",
2720 : "DOLLAR",
2721 : "EXACT",
2722 : "FILTERXML",
2723 : "FIND",
2724 : "FIXED",
2725 : "JIS",
2726 : "LEFT",
2727 : "LEFTB",
2728 : "LEN",
2729 : "LENB",
2730 : "LOWER",
2731 : "MID",
2732 : "MIDB",
2733 : "NUMBERVALUE",
2734 : "PROPER",
2735 : "REPLACE",
2736 : "REPT",
2737 : "RIGHT",
2738 : "RIGHTB",
2739 : "ROMAN",
2740 : "SEARCH",
2741 : "SUBSTITUTE",
2742 : "T",
2743 : "TEXT",
2744 : "TRIM",
2745 : "UNICHAR",
2746 : "UNICODE",
2747 : "UPPER",
2748 : "VALUE",
2749 : "WEBSERVICE",
2750 : 0
2751 1 : };
2752 :
2753 : struct {
2754 : const char* Category; const char** Functions;
2755 : } aTests[] = {
2756 : { "Database", aDataBase },
2757 : { "Date&Time", aDateTime },
2758 : { "Financial", aFinancial },
2759 : { "Information", aInformation },
2760 : { "Logical", aLogical },
2761 : { "Mathematical", aMathematical },
2762 : { "Array", aArray },
2763 : { "Statistical", aStatistical },
2764 : { "Spreadsheet", aSpreadsheet },
2765 : { "Text", aText },
2766 : { "Add-in", 0 },
2767 : { 0, 0 }
2768 1 : };
2769 :
2770 1 : ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
2771 1 : sal_uInt32 n = pFuncMgr->getCount();
2772 12 : for (sal_uInt32 i = 0; i < n; ++i)
2773 : {
2774 11 : const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
2775 11 : CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
2776 11 : sal_uInt32 nFuncCount = pCat->getCount();
2777 370 : for (sal_uInt32 j = 0; j < nFuncCount; ++j)
2778 : {
2779 359 : const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
2780 359 : CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", OUString::createFromAscii(aTests[i].Functions[j]), pFunc->getFunctionName());
2781 : }
2782 : }
2783 1 : }
2784 :
2785 1 : void Test::testGraphicsInGroup()
2786 : {
2787 1 : m_pDoc->InsertTab(0, "TestTab");
2788 1 : CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
2789 : SCROW nRow1, nRow2;
2790 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2791 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
2792 :
2793 1 : m_pDoc->InitDrawLayer();
2794 1 : ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
2795 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
2796 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
2797 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
2798 :
2799 : {
2800 : //Add a square
2801 1 : Rectangle aOrigRect(2,2,100,100);
2802 1 : SdrRectObj *pObj = new SdrRectObj(aOrigRect);
2803 1 : pPage->InsertObject(pObj);
2804 1 : const Rectangle &rNewRect = pObj->GetLogicRect();
2805 1 : CPPUNIT_ASSERT_MESSAGE("must have equal position and size", aOrigRect == rNewRect);
2806 :
2807 1 : ScDrawLayer::SetPageAnchored(*pObj);
2808 :
2809 : //Use a range of rows guaranteed to include all of the square
2810 1 : m_pDoc->ShowRows(0, 100, 0, false);
2811 1 : m_pDoc->SetDrawPageSize(0);
2812 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
2813 1 : m_pDoc->ShowRows(0, 100, 0, true);
2814 1 : m_pDoc->SetDrawPageSize(0);
2815 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
2816 :
2817 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
2818 1 : CPPUNIT_ASSERT_MESSAGE("That shouldn't change size or positioning", aOrigRect == rNewRect);
2819 :
2820 1 : m_pDoc->ShowRows(0, 100, 0, false);
2821 1 : m_pDoc->SetDrawPageSize(0);
2822 2 : CPPUNIT_ASSERT_MESSAGE("Left and Right should be unchanged",
2823 1 : aOrigRect.Left() == rNewRect.Left() && aOrigRect.Right() == rNewRect.Right());
2824 2 : CPPUNIT_ASSERT_MESSAGE("Height should be minimum allowed height",
2825 1 : (rNewRect.Bottom() - rNewRect.Top()) <= 1);
2826 1 : m_pDoc->ShowRows(0, 100, 0, true);
2827 1 : m_pDoc->SetDrawPageSize(0);
2828 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
2829 : }
2830 :
2831 : {
2832 : // Add a circle.
2833 1 : Rectangle aOrigRect = Rectangle(10,10,210,210); // 200 x 200
2834 1 : SdrCircObj* pObj = new SdrCircObj(OBJ_CIRC, aOrigRect);
2835 1 : pPage->InsertObject(pObj);
2836 1 : const Rectangle& rNewRect = pObj->GetLogicRect();
2837 2 : CPPUNIT_ASSERT_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
2838 1 : aOrigRect == rNewRect);
2839 :
2840 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
2841 2 : CPPUNIT_ASSERT_MESSAGE("Size changed when cell anchored. Not good.",
2842 1 : aOrigRect == rNewRect);
2843 :
2844 : // Insert 2 rows at the top. This should push the circle object down.
2845 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
2846 1 : m_pDoc->SetDrawPageSize(0);
2847 :
2848 : // Make sure the size of the circle is still identical.
2849 2 : CPPUNIT_ASSERT_MESSAGE("Size of the circle has changed, but shouldn't!",
2850 1 : aOrigRect.GetSize() == rNewRect.GetSize());
2851 :
2852 : // Delete 2 rows at the top. This should bring the circle object to its original position.
2853 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
2854 1 : m_pDoc->SetDrawPageSize(0);
2855 1 : CPPUNIT_ASSERT_MESSAGE("Failed to move back to its original position.", aOrigRect == rNewRect);
2856 : }
2857 :
2858 : {
2859 : // Add a line.
2860 1 : basegfx::B2DPolygon aTempPoly;
2861 1 : Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
2862 1 : Rectangle aOrigRect(10,200,110,300); // 100 x 100
2863 1 : aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
2864 1 : aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
2865 1 : SdrPathObj* pObj = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
2866 1 : pObj->NbcSetLogicRect(aOrigRect);
2867 1 : pPage->InsertObject(pObj);
2868 1 : const Rectangle& rNewRect = pObj->GetLogicRect();
2869 1 : CPPUNIT_ASSERT_MESSAGE("Size differ.", aOrigRect == rNewRect);
2870 :
2871 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
2872 2 : CPPUNIT_ASSERT_MESSAGE("Size changed when cell-anchored. Not good.",
2873 1 : aOrigRect == rNewRect);
2874 :
2875 : // Insert 2 rows at the top and delete them immediately.
2876 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
2877 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
2878 1 : m_pDoc->SetDrawPageSize(0);
2879 2 : CPPUNIT_ASSERT_MESSAGE("Size of a line object changed after row insertion and removal.",
2880 1 : aOrigRect == rNewRect);
2881 :
2882 1 : sal_Int32 n = pObj->GetPointCount();
2883 1 : CPPUNIT_ASSERT_MESSAGE("There should be exactly 2 points in a line object.", n == 2);
2884 2 : CPPUNIT_ASSERT_MESSAGE("Line shape has changed.",
2885 2 : aStartPos == pObj->GetPoint(0) && aEndPos == pObj->GetPoint(1));
2886 : }
2887 :
2888 1 : m_pDoc->DeleteTab(0);
2889 1 : }
2890 :
2891 1 : void Test::testGraphicsOnSheetMove()
2892 : {
2893 1 : m_pDoc->InsertTab(0, "Tab1");
2894 1 : m_pDoc->InsertTab(1, "Tab2");
2895 1 : CPPUNIT_ASSERT_MESSAGE("There should be only 2 sheets to begin with", m_pDoc->GetTableCount() == 2);
2896 :
2897 1 : m_pDoc->InitDrawLayer();
2898 1 : ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
2899 1 : CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
2900 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
2901 1 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
2902 :
2903 : // Insert an object.
2904 1 : Rectangle aObjRect(2,2,100,100);
2905 1 : SdrObject* pObj = new SdrRectObj(aObjRect);
2906 1 : pPage->InsertObject(pObj);
2907 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
2908 :
2909 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(1));
2910 :
2911 1 : const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
2912 1 : CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
2913 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
2914 :
2915 1 : pPage = pDrawLayer->GetPage(1);
2916 1 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
2917 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(0));
2918 :
2919 : // Insert a new sheet at left-end, and make sure the object has moved to
2920 : // the 2nd page.
2921 1 : m_pDoc->InsertTab(0, "NewTab");
2922 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(3));
2923 1 : pPage = pDrawLayer->GetPage(0);
2924 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
2925 1 : pPage = pDrawLayer->GetPage(1);
2926 1 : CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
2927 1 : pPage = pDrawLayer->GetPage(2);
2928 1 : CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage && pPage->GetObjCount() == 0);
2929 :
2930 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
2931 :
2932 : // Now, delete the sheet that just got inserted. The object should be back
2933 : // on the 1st sheet.
2934 1 : m_pDoc->DeleteTab(0);
2935 1 : pPage = pDrawLayer->GetPage(0);
2936 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage && pPage->GetObjCount() == 1);
2937 2 : CPPUNIT_ASSERT_MESSAGE("Size and position of the object shouldn't change.",
2938 1 : pObj->GetLogicRect() == aObjRect);
2939 :
2940 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
2941 :
2942 : // Move the 1st sheet to the last position.
2943 1 : m_pDoc->MoveTab(0, 1);
2944 1 : pPage = pDrawLayer->GetPage(0);
2945 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
2946 1 : pPage = pDrawLayer->GetPage(1);
2947 1 : CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
2948 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
2949 :
2950 : // Copy the 2nd sheet, which has one drawing object to the last position.
2951 1 : m_pDoc->CopyTab(1, 2);
2952 1 : pPage = pDrawLayer->GetPage(2);
2953 1 : CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage && pPage->GetObjCount() == 1);
2954 1 : pObj = pPage->GetObj(0);
2955 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
2956 1 : pData = ScDrawLayer::GetObjData(pObj);
2957 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
2958 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 2 && pData->maEnd.Tab() == 2);
2959 :
2960 1 : m_pDoc->DeleteTab(2);
2961 1 : m_pDoc->DeleteTab(1);
2962 1 : m_pDoc->DeleteTab(0);
2963 1 : }
2964 :
2965 1 : void Test::testToggleRefFlag()
2966 : {
2967 : // In this test, there is no need to insert formula string into a cell in
2968 : // the document, as ScRefFinder does not depend on the content of the
2969 : // document except for the sheet names.
2970 :
2971 1 : m_pDoc->InsertTab(0, "Test");
2972 :
2973 : {
2974 : // Calc A1: basic 2D reference
2975 :
2976 1 : OUString aFormula("=B100");
2977 1 : ScAddress aPos(1, 5, 0);
2978 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
2979 :
2980 : // Original
2981 1 : CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
2982 :
2983 : // column relative / row relative -> column absolute / row absolute
2984 1 : aFinder.ToggleRel(0, aFormula.getLength());
2985 1 : aFormula = aFinder.GetText();
2986 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B$100" );
2987 :
2988 : // column absolute / row absolute -> column relative / row absolute
2989 1 : aFinder.ToggleRel(0, aFormula.getLength());
2990 1 : aFormula = aFinder.GetText();
2991 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B$100" );
2992 :
2993 : // column relative / row absolute -> column absolute / row relative
2994 1 : aFinder.ToggleRel(0, aFormula.getLength());
2995 1 : aFormula = aFinder.GetText();
2996 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B100" );
2997 :
2998 : // column absolute / row relative -> column relative / row relative
2999 1 : aFinder.ToggleRel(0, aFormula.getLength());
3000 1 : aFormula = aFinder.GetText();
3001 2 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B100" );
3002 : }
3003 :
3004 : {
3005 : // Excel R1C1: basic 2D reference
3006 :
3007 1 : OUString aFormula("=R2C1");
3008 1 : ScAddress aPos(3, 5, 0);
3009 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3010 :
3011 : // Original
3012 1 : CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
3013 :
3014 : // column absolute / row absolute -> column relative / row absolute
3015 1 : aFinder.ToggleRel(0, aFormula.getLength());
3016 1 : aFormula = aFinder.GetText();
3017 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R2C[-3]"), aFormula);
3018 :
3019 : // column relative / row absolute - > column absolute / row relative
3020 1 : aFinder.ToggleRel(0, aFormula.getLength());
3021 1 : aFormula = aFinder.GetText();
3022 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C1"), aFormula);
3023 :
3024 : // column absolute / row relative -> column relative / row relative
3025 1 : aFinder.ToggleRel(0, aFormula.getLength());
3026 1 : aFormula = aFinder.GetText();
3027 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C[-3]"), aFormula);
3028 :
3029 : // column relative / row relative -> column absolute / row absolute
3030 1 : aFinder.ToggleRel(0, aFormula.getLength());
3031 1 : aFormula = aFinder.GetText();
3032 2 : CPPUNIT_ASSERT_EQUAL(OUString("=R2C1"), aFormula);
3033 : }
3034 :
3035 : {
3036 : // Excel R1C1: Selection at the end of the formula string and does not
3037 : // overlap the formula string at all (inspired by fdo#39135).
3038 1 : OUString aFormula("=R1C1");
3039 1 : ScAddress aPos(1, 1, 0);
3040 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3041 :
3042 : // Original
3043 1 : CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3044 :
3045 : // Make the column relative.
3046 1 : sal_Int32 n = aFormula.getLength();
3047 1 : aFinder.ToggleRel(n, n);
3048 1 : aFormula = aFinder.GetText();
3049 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R1C[-1]"), aFormula);
3050 :
3051 : // Make the row relative.
3052 1 : n = aFormula.getLength();
3053 1 : aFinder.ToggleRel(n, n);
3054 1 : aFormula = aFinder.GetText();
3055 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C1"), aFormula);
3056 :
3057 : // Make both relative.
3058 1 : n = aFormula.getLength();
3059 1 : aFinder.ToggleRel(n, n);
3060 1 : aFormula = aFinder.GetText();
3061 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C[-1]"), aFormula);
3062 :
3063 : // Back to the original.
3064 1 : n = aFormula.getLength();
3065 1 : aFinder.ToggleRel(n, n);
3066 1 : aFormula = aFinder.GetText();
3067 2 : CPPUNIT_ASSERT_EQUAL(OUString("=R1C1"), aFormula);
3068 : }
3069 :
3070 : {
3071 : // Calc A1:
3072 1 : OUString aFormula("=A1+4");
3073 1 : ScAddress aPos(1, 1, 0);
3074 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
3075 :
3076 : // Original
3077 1 : CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3078 :
3079 : // Set the cursor over the 'A1' part and toggle.
3080 1 : aFinder.ToggleRel(2, 2);
3081 1 : aFormula = aFinder.GetText();
3082 1 : CPPUNIT_ASSERT_EQUAL(OUString("=$A$1+4"), aFormula);
3083 :
3084 1 : aFinder.ToggleRel(2, 2);
3085 1 : aFormula = aFinder.GetText();
3086 1 : CPPUNIT_ASSERT_EQUAL(OUString("=A$1+4"), aFormula);
3087 :
3088 1 : aFinder.ToggleRel(2, 2);
3089 1 : aFormula = aFinder.GetText();
3090 1 : CPPUNIT_ASSERT_EQUAL(OUString("=$A1+4"), aFormula);
3091 :
3092 1 : aFinder.ToggleRel(2, 2);
3093 1 : aFormula = aFinder.GetText();
3094 2 : CPPUNIT_ASSERT_EQUAL(OUString("=A1+4"), aFormula);
3095 : }
3096 :
3097 : // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
3098 : // partial selection within formula string.
3099 :
3100 1 : m_pDoc->DeleteTab(0);
3101 1 : }
3102 :
3103 1 : void Test::testAutofilter()
3104 : {
3105 1 : OUString aDBName("NONAME");
3106 :
3107 1 : m_pDoc->InsertTab( 0, "Test" );
3108 :
3109 : // cell contents (0 = empty cell)
3110 : const char* aData[][3] = {
3111 : { "C1", "C2", "C3" },
3112 : { "0", "1", "A" },
3113 : { "1", "2", 0 },
3114 : { "1", "2", "B" },
3115 : { "0", "2", "B" }
3116 1 : };
3117 :
3118 1 : SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
3119 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
3120 :
3121 : // Populate cells.
3122 6 : for (SCROW i = 0; i < nRows; ++i)
3123 20 : for (SCCOL j = 0; j < nCols; ++j)
3124 15 : if (aData[i][j])
3125 14 : m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
3126 :
3127 1 : ScDBData* pDBData = new ScDBData(aDBName, 0, 0, 0, nCols-1, nRows-1);
3128 1 : m_pDoc->SetAnonymousDBData(0,pDBData);
3129 :
3130 1 : pDBData->SetAutoFilter(true);
3131 1 : ScRange aRange;
3132 1 : pDBData->GetArea(aRange);
3133 1 : m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3134 1 : aRange.aEnd.Col(), aRange.aStart.Row(),
3135 3 : aRange.aStart.Tab(), SC_MF_AUTO);
3136 :
3137 : //create the query param
3138 2 : ScQueryParam aParam;
3139 1 : pDBData->GetQueryParam(aParam);
3140 1 : ScQueryEntry& rEntry = aParam.GetEntry(0);
3141 1 : rEntry.bDoQuery = true;
3142 1 : rEntry.nField = 0;
3143 1 : rEntry.eOp = SC_EQUAL;
3144 1 : rEntry.GetQueryItem().mfVal = 0;
3145 : // add queryParam to database range.
3146 1 : pDBData->SetQueryParam(aParam);
3147 :
3148 : // perform the query.
3149 1 : m_pDoc->Query(0, aParam, true);
3150 :
3151 : //control output
3152 : SCROW nRow1, nRow2;
3153 1 : bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3154 1 : CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden && nRow1 == 2 && nRow2 == 3);
3155 :
3156 : // Remove filtering.
3157 1 : rEntry.Clear();
3158 1 : m_pDoc->Query(0, aParam, true);
3159 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3160 1 : CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
3161 :
3162 : // Filter for non-empty cells by column C.
3163 1 : rEntry.bDoQuery = true;
3164 1 : rEntry.nField = 2;
3165 1 : rEntry.SetQueryByNonEmpty();
3166 1 : m_pDoc->Query(0, aParam, true);
3167 :
3168 : // only row 3 should be hidden. The rest should be visible.
3169 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3170 1 : CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 1);
3171 1 : bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3172 1 : CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden && nRow1 == 2 && nRow2 == 2);
3173 1 : bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3174 1 : CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden && nRow1 == 3 && nRow2 == MAXROW);
3175 :
3176 : // Now, filter for empty cells by column C.
3177 1 : rEntry.SetQueryByEmpty();
3178 1 : m_pDoc->Query(0, aParam, true);
3179 :
3180 : // Now, only row 1 and 3, and 6 and down should be visible.
3181 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3182 1 : CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 0);
3183 1 : bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
3184 1 : CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden && nRow1 == 1 && nRow2 == 1);
3185 1 : bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3186 1 : CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden && nRow1 == 2 && nRow2 == 2);
3187 1 : bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3188 1 : CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden && nRow1 == 3 && nRow2 == 4);
3189 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3190 1 : CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden && nRow1 == 5 && nRow2 == MAXROW);
3191 :
3192 2 : m_pDoc->DeleteTab(0);
3193 1 : }
3194 :
3195 1 : void Test::testCopyPaste()
3196 : {
3197 1 : m_pDoc->InsertTab(0, "Sheet1");
3198 1 : m_pDoc->InsertTab(1, "Sheet2");
3199 : //test copy&paste + ScUndoPaste
3200 : //copy local and global range names in formulas
3201 : //string cells and value cells
3202 1 : m_pDoc->SetValue(0, 0, 0, 1);
3203 1 : m_pDoc->SetValue(3, 0, 0, 0);
3204 1 : m_pDoc->SetValue(3, 1, 0, 1);
3205 1 : m_pDoc->SetValue(3, 2, 0, 2);
3206 1 : m_pDoc->SetValue(3, 3, 0, 3);
3207 1 : m_pDoc->SetString(2, 0, 0, "test");
3208 1 : ScAddress aAdr (0, 0, 0);
3209 :
3210 : //create some range names, local and global
3211 1 : ScRangeData* pLocal1 = new ScRangeData(m_pDoc, OUString("local1"), aAdr);
3212 1 : ScRangeData* pLocal2 = new ScRangeData(m_pDoc, OUString("local2"), aAdr);
3213 1 : ScRangeData* pGlobal = new ScRangeData(m_pDoc, OUString("global"), aAdr);
3214 1 : ScRangeName* pGlobalRangeName = new ScRangeName();
3215 1 : pGlobalRangeName->insert(pGlobal);
3216 1 : ScRangeName* pLocalRangeName1 = new ScRangeName();
3217 1 : pLocalRangeName1->insert(pLocal1);
3218 1 : pLocalRangeName1->insert(pLocal2);
3219 1 : m_pDoc->SetRangeName(pGlobalRangeName);
3220 1 : m_pDoc->SetRangeName(0, pLocalRangeName1);
3221 :
3222 : // Add formula to B1.
3223 1 : OUString aFormulaString("=local1+global+SUM($C$1:$D$4)");
3224 1 : m_pDoc->SetString(1, 0, 0, aFormulaString);
3225 :
3226 1 : double fValue = m_pDoc->GetValue(ScAddress(1,0,0));
3227 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 8", fValue, 8);
3228 :
3229 : // add notes to A1:C1
3230 1 : ScAddress aAdrA1 (0, 0, 0); // empty cell content
3231 2 : OUString aHelloA1("Hello world in A1");
3232 1 : ScPostIt* pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
3233 1 : pNoteA1->SetText(aAdrA1, aHelloA1);
3234 1 : ScAddress aAdrB1 (1, 0, 0); // formula cell content
3235 2 : OUString aHelloB1("Hello world in B1");
3236 1 : ScPostIt* pNoteB1 = m_pDoc->GetOrCreateNote(aAdrB1);
3237 1 : pNoteB1->SetText(aAdrB1, aHelloB1);
3238 1 : ScAddress aAdrC1 (2, 0, 0); // string cell content
3239 2 : OUString aHelloC1("Hello world in C1");
3240 1 : ScPostIt* pNoteC1 = m_pDoc->GetOrCreateNote(aAdrC1);
3241 1 : pNoteC1->SetText(aAdrC1, aHelloC1);
3242 :
3243 : //copy Sheet1.A1:C1 to Sheet2.A2:C2
3244 1 : ScRange aRange(0,0,0,2,0,0);
3245 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
3246 1 : copyToClip(m_pDoc, aRange, &aClipDoc);
3247 :
3248 1 : aRange = ScRange(0,1,1,2,1,1);//target: Sheet2.A2:C2
3249 1 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
3250 1 : pUndoDoc->InitUndo(m_pDoc, 1, 1, true, true);
3251 2 : boost::scoped_ptr<ScUndoPaste> pUndo(createUndoPaste(getDocShell(), aRange, pUndoDoc));
3252 2 : ScMarkData aMark;
3253 1 : aMark.SetMarkArea(aRange);
3254 1 : m_pDoc->CopyFromClip(aRange, aMark, IDF_ALL, NULL, &aClipDoc);
3255 :
3256 : //check values after copying
3257 2 : OUString aString;
3258 1 : fValue = m_pDoc->GetValue(ScAddress(1,1,1));
3259 1 : m_pDoc->GetFormula(1,1,1, aString);
3260 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("copied formula should return 2", fValue, 2);
3261 1 : CPPUNIT_ASSERT_MESSAGE("formula string was not copied correctly", aString == aFormulaString);
3262 1 : fValue = m_pDoc->GetValue(ScAddress(0,1,1));
3263 1 : CPPUNIT_ASSERT_MESSAGE("copied value should be 1", fValue == 1);
3264 :
3265 : //check local range name after copying
3266 1 : pLocal1 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL1"));
3267 1 : CPPUNIT_ASSERT_MESSAGE("local range name 1 should be copied", pLocal1);
3268 1 : ScRange aRangeLocal1;
3269 1 : bool bIsValidRef = pLocal1->IsValidReference(aRangeLocal1);
3270 1 : CPPUNIT_ASSERT_MESSAGE("local range name 1 should be valid", bIsValidRef);
3271 1 : CPPUNIT_ASSERT_MESSAGE("local range 1 should still point to Sheet1.A1",aRangeLocal1 == ScRange(0,0,0,0,0,0));
3272 1 : pLocal2 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
3273 1 : CPPUNIT_ASSERT_MESSAGE("local2 should not be copied", pLocal2 == NULL);
3274 :
3275 : // check notes after copying
3276 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.A2", m_pDoc->HasNote(ScAddress(0, 1, 1)));
3277 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.B2", m_pDoc->HasNote(ScAddress(1, 1, 1)));
3278 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.C2", m_pDoc->HasNote(ScAddress(2, 1, 1)));
3279 2 : CPPUNIT_ASSERT_MESSAGE("Note content on Sheet1.A1 not copied to Sheet2.A2, empty cell content",
3280 1 : m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(0, 1, 1))->GetText());
3281 2 : CPPUNIT_ASSERT_MESSAGE("Note content on Sheet1.B1 not copied to Sheet2.B2, formula cell content",
3282 1 : m_pDoc->GetNote(ScAddress(1, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(1, 1, 1))->GetText());
3283 2 : CPPUNIT_ASSERT_MESSAGE("Note content on Sheet1.C1 not copied to Sheet2.C2, string cell content",
3284 1 : m_pDoc->GetNote(ScAddress(2, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(2, 1, 1))->GetText());
3285 :
3286 : //check undo and redo
3287 1 : pUndo->Undo();
3288 1 : fValue = m_pDoc->GetValue(ScAddress(1,1,1));
3289 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", fValue, 0);
3290 1 : aString = m_pDoc->GetString(2, 1, 1);
3291 1 : CPPUNIT_ASSERT_MESSAGE("after undo, string should be removed", aString == "");
3292 1 : CPPUNIT_ASSERT_MESSAGE("after undo, note on A2 should be removed", !m_pDoc->HasNote(ScAddress(0, 1, 1)));
3293 1 : CPPUNIT_ASSERT_MESSAGE("after undo, note on B2 should be removed", !m_pDoc->HasNote(ScAddress(1, 1, 1)));
3294 1 : CPPUNIT_ASSERT_MESSAGE("after undo, note on C2 should be removed", !m_pDoc->HasNote(ScAddress(2, 1, 1)));
3295 :
3296 1 : pUndo->Redo();
3297 1 : fValue = m_pDoc->GetValue(ScAddress(1,1,1));
3298 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 2 after redo", fValue, 2);
3299 1 : aString = m_pDoc->GetString(2, 1, 1);
3300 1 : CPPUNIT_ASSERT_MESSAGE("Cell Sheet2.C2 should contain: test", aString == "test");
3301 1 : m_pDoc->GetFormula(1,1,1, aString);
3302 1 : CPPUNIT_ASSERT_MESSAGE("Formula should be correct again", aString == aFormulaString);
3303 :
3304 1 : CPPUNIT_ASSERT_MESSAGE("After Redo, there should be a note on Sheet2.A2", m_pDoc->HasNote(ScAddress(0, 1, 1)));
3305 1 : CPPUNIT_ASSERT_MESSAGE("After Redo, there should be a note on Sheet2.B2", m_pDoc->HasNote(ScAddress(1, 1, 1)));
3306 1 : CPPUNIT_ASSERT_MESSAGE("After Redo, there should be a note on Sheet2.C2", m_pDoc->HasNote(ScAddress(2, 1, 1)));
3307 2 : CPPUNIT_ASSERT_MESSAGE("After Redo, note again on Sheet2.A2, empty cell content",
3308 1 : m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(0, 1, 1))->GetText());
3309 2 : CPPUNIT_ASSERT_MESSAGE("After Redo, note again on Sheet2.B2, formula cell content",
3310 1 : m_pDoc->GetNote(ScAddress(1, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(1, 1, 1))->GetText());
3311 2 : CPPUNIT_ASSERT_MESSAGE("After Redo, note again on Sheet2.C2, string cell content",
3312 1 : m_pDoc->GetNote(ScAddress(2, 0, 0))->GetText() == m_pDoc->GetNote(ScAddress(2, 1, 1))->GetText());
3313 :
3314 :
3315 1 : m_pDoc->DeleteTab(1);
3316 2 : m_pDoc->DeleteTab(0);
3317 1 : }
3318 :
3319 1 : void Test::testCopyPasteAsLink()
3320 : {
3321 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
3322 :
3323 1 : m_pDoc->InsertTab(0, "Sheet1");
3324 1 : m_pDoc->InsertTab(1, "Sheet2");
3325 :
3326 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1); // A1
3327 1 : m_pDoc->SetValue(ScAddress(0,1,0), 2); // A2
3328 1 : m_pDoc->SetValue(ScAddress(0,2,0), 3); // A3
3329 :
3330 1 : ScRange aRange(0,0,0,0,2,0); // Copy A1:A3 to clip.
3331 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
3332 1 : copyToClip(m_pDoc, aRange, &aClipDoc);
3333 :
3334 1 : aRange = ScRange(1,1,1,1,3,1); // Paste to B2:B4 on Sheet2.
3335 2 : ScMarkData aMark;
3336 1 : aMark.SetMarkArea(aRange);
3337 : // Paste range as link.
3338 1 : m_pDoc->CopyFromClip(aRange, aMark, IDF_CONTENTS, NULL, &aClipDoc, true, true);
3339 :
3340 : // Check pasted content to make sure they reference the correct cells.
3341 1 : ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,1));
3342 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3343 1 : CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());
3344 :
3345 1 : pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,1));
3346 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3347 1 : CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());
3348 :
3349 1 : pFC = m_pDoc->GetFormulaCell(ScAddress(1,3,1));
3350 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3351 1 : CPPUNIT_ASSERT_EQUAL(3.0, pFC->GetValue());
3352 :
3353 1 : m_pDoc->DeleteTab(1);
3354 2 : m_pDoc->DeleteTab(0);
3355 1 : }
3356 :
3357 1 : void Test::testCopyPasteTranspose()
3358 : {
3359 1 : m_pDoc->InsertTab(0, "Sheet1");
3360 1 : m_pDoc->InsertTab(1, "Sheet2");
3361 :
3362 1 : m_pDoc->SetValue(0, 0, 0, 1);
3363 1 : m_pDoc->SetString(1, 0, 0, "=A1+1");
3364 1 : m_pDoc->SetString(2, 0, 0, "test");
3365 :
3366 : // add notes to A1:C1
3367 1 : ScAddress aAdrA1 (0, 0, 0); // numerical cell content
3368 1 : OUString aHelloA1("Hello world in A1");
3369 1 : ScPostIt* pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
3370 1 : pNoteA1->SetText(aAdrA1, aHelloA1);
3371 1 : ScAddress aAdrB1 (1, 0, 0); // formula cell content
3372 2 : OUString aHelloB1("Hello world in B1");
3373 1 : ScPostIt* pNoteB1 = m_pDoc->GetOrCreateNote(aAdrB1);
3374 1 : pNoteB1->SetText(aAdrB1, aHelloB1);
3375 1 : ScAddress aAdrC1 (2, 0, 0); // string cell content
3376 2 : OUString aHelloC1("Hello world in C1");
3377 1 : ScPostIt* pNoteC1 = m_pDoc->GetOrCreateNote(aAdrC1);
3378 1 : pNoteC1->SetText(aAdrC1, aHelloC1);
3379 :
3380 : // transpose clipboard, paste and check on Sheet2
3381 1 : m_pDoc->InsertTab(1, "Sheet2");
3382 :
3383 1 : ScRange aSrcRange = ScRange(0,0,0,2,0,0);
3384 2 : ScDocument aNewClipDoc(SCDOCMODE_CLIP);
3385 1 : copyToClip(m_pDoc, aSrcRange, &aNewClipDoc);
3386 :
3387 2 : ::std::auto_ptr<ScDocument> pTransClip;
3388 1 : pTransClip.reset(new ScDocument(SCDOCMODE_CLIP));
3389 1 : aNewClipDoc.TransposeClip(pTransClip.get(), IDF_ALL, false);
3390 1 : ScDocument* pTransposedClip = pTransClip.release();
3391 :
3392 1 : ScRange aDestRange = ScRange(3,1,1,3,3,1);//target: Sheet2.D2:D4
3393 2 : ScMarkData aMark;
3394 1 : aMark.SetMarkArea(aDestRange);
3395 1 : m_pDoc->CopyFromClip(aDestRange, aMark, IDF_ALL, NULL, pTransposedClip);
3396 :
3397 : //check cell content after transposed copy/paste
3398 2 : OUString aString = m_pDoc->GetString(3, 3, 1);
3399 1 : CPPUNIT_ASSERT_MESSAGE("Cell Sheet2.D4 should contain: test", aString == "test");
3400 1 : double fValue = m_pDoc->GetValue(ScAddress(3,1,1));
3401 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("transposed copied cell should return 1", fValue, 1);
3402 1 : fValue = m_pDoc->GetValue(ScAddress(3,2,1));
3403 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("transposed copied formula should return 2", fValue, 2);
3404 1 : m_pDoc->GetFormula(3, 2, 1, aString);
3405 1 : CPPUNIT_ASSERT_MESSAGE("transposed formula should point on Sheet2.D2", aString == "=D2+1");
3406 :
3407 : // check notes after transposed copy/paste
3408 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D2", m_pDoc->HasNote(ScAddress(3, 1, 1)));
3409 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D3", m_pDoc->HasNote(ScAddress(3, 2, 1)));
3410 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D4", m_pDoc->HasNote(ScAddress(3, 3, 1)));
3411 2 : CPPUNIT_ASSERT_MESSAGE("Content of cell note on Sheet2.D2",
3412 1 : m_pDoc->GetNote(ScAddress(3, 1, 1))->GetText() == m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText());
3413 2 : CPPUNIT_ASSERT_MESSAGE("Content of cell note on Sheet2.D3",
3414 1 : m_pDoc->GetNote(ScAddress(3, 2, 1))->GetText() == m_pDoc->GetNote(ScAddress(1, 0, 0))->GetText());
3415 2 : CPPUNIT_ASSERT_MESSAGE("Content of cell note on Sheet2.D4",
3416 1 : m_pDoc->GetNote(ScAddress(3, 3, 1))->GetText() == m_pDoc->GetNote(ScAddress(2, 0, 0))->GetText());
3417 :
3418 1 : m_pDoc->DeleteTab(1);
3419 2 : m_pDoc->DeleteTab(0);
3420 :
3421 1 : }
3422 :
3423 1 : void Test::testCopyPasteMultiRange()
3424 : {
3425 1 : m_pDoc->InsertTab(0, "Test");
3426 :
3427 : // Fill A2:B6 with numbers.
3428 6 : for (SCROW nRow = 1; nRow <= 5; ++nRow)
3429 : {
3430 15 : for (SCCOL nCol = 0; nCol <= 1; ++nCol)
3431 : {
3432 10 : ScAddress aPos(nCol,nRow,0);
3433 10 : m_pDoc->SetValue(aPos, nRow+nCol);
3434 : }
3435 : }
3436 :
3437 : // Fill D9:E11 with numbers.
3438 4 : for (SCROW nRow = 8; nRow <= 10; ++nRow)
3439 : {
3440 9 : for (SCCOL nCol = 3; nCol <= 4; ++nCol)
3441 : {
3442 6 : ScAddress aPos(nCol,nRow,0);
3443 6 : m_pDoc->SetValue(aPos, 10.0);
3444 : }
3445 : }
3446 :
3447 1 : ScMarkData aMark;
3448 1 : aMark.SelectOneTable(0);
3449 :
3450 : // Copy A2:B2, A4:B4, and A6:B6 to clipboard.
3451 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
3452 2 : ScClipParam aClipParam;
3453 1 : aClipParam.maRanges.Append(ScRange(0,1,0,1,1,0)); // A2:B2
3454 1 : aClipParam.maRanges.Append(ScRange(0,3,0,1,3,0)); // A4:B4
3455 1 : aClipParam.maRanges.Append(ScRange(0,5,0,1,5,0)); // A6:B6
3456 1 : aClipParam.meDirection = ScClipParam::Row;
3457 1 : m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
3458 :
3459 : // Paste to D9:E11, and make sure it won't crash (rhbz#1080196).
3460 1 : m_pDoc->CopyMultiRangeFromClip(ScAddress(3,8,0), aMark, IDF_CONTENTS, &aClipDoc);
3461 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,8,0)));
3462 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,8,0)));
3463 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,9,0)));
3464 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(4,9,0)));
3465 1 : CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(3,10,0)));
3466 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(4,10,0)));
3467 :
3468 2 : m_pDoc->DeleteTab(0);
3469 1 : }
3470 :
3471 1 : void Test::testCopyPasteSkipEmpty()
3472 : {
3473 : struct Check
3474 : {
3475 : const char* mpStr;
3476 : Color maColor;
3477 : bool mbHasNote;
3478 : };
3479 :
3480 : struct TestRange
3481 : {
3482 : ScDocument* mpDoc;
3483 :
3484 1 : TestRange( ScDocument* pDoc ) : mpDoc(pDoc) {}
3485 :
3486 4 : bool checkRange( const ScAddress& rPos, const Check* p, const Check* pEnd )
3487 : {
3488 4 : ScAddress aPos(rPos);
3489 4 : OUString aPosStr = aPos.Format(SCA_VALID);
3490 24 : for (; p != pEnd; ++p, aPos.IncRow())
3491 : {
3492 20 : if (!mpDoc->GetString(aPos).equalsAscii(p->mpStr))
3493 : {
3494 0 : cerr << aPosStr << ": incorrect string value: expected='" << p->mpStr << "' actual='" << mpDoc->GetString(aPos) << endl;
3495 0 : return false;
3496 : }
3497 :
3498 : const SvxBrushItem* pBrush =
3499 20 : dynamic_cast<const SvxBrushItem*>(mpDoc->GetAttr(aPos, ATTR_BACKGROUND));
3500 :
3501 20 : if (!pBrush)
3502 : {
3503 0 : cerr << aPosStr << ": failed to get brush item from the cell." << endl;
3504 0 : return false;
3505 : }
3506 :
3507 20 : if (pBrush->GetColor() != p->maColor)
3508 : {
3509 0 : Color aExpected = p->maColor;
3510 0 : Color aActual = pBrush->GetColor();
3511 0 : cerr << aPosStr << ": incorrect cell background color: expected=("
3512 0 : << static_cast<int>(aExpected.GetRed()) << ","
3513 0 : << static_cast<int>(aExpected.GetGreen()) << ","
3514 0 : << static_cast<int>(aExpected.GetBlue()) << "), actual=("
3515 0 : << static_cast<int>(aActual.GetRed()) << ","
3516 0 : << static_cast<int>(aActual.GetGreen()) << ","
3517 0 : << static_cast<int>(aActual.GetBlue()) << ")" << endl;
3518 :
3519 0 : return false;
3520 : }
3521 :
3522 20 : bool bHasNote = mpDoc->HasNote(aPos);
3523 20 : if (bHasNote != p->mbHasNote)
3524 : {
3525 0 : cerr << aPosStr << ": ";
3526 0 : if (p->mbHasNote)
3527 0 : cerr << "this cell should have a cell note, but doesn't." << endl;
3528 : else
3529 0 : cerr << "this cell should NOT have a cell note, but one is found." << endl;
3530 :
3531 0 : return false;
3532 : }
3533 : }
3534 :
3535 4 : return true;
3536 : }
3537 :
3538 1 : } aTest(m_pDoc);
3539 :
3540 1 : m_pDoc->InsertTab(0, "Test");
3541 1 : m_pDoc->InitDrawLayer(&getDocShell()); // for cell note objects.
3542 :
3543 1 : ScRange aSrcRange(0,0,0,0,4,0);
3544 1 : ScRange aDestRange(1,0,0,1,4,0);
3545 :
3546 1 : ScMarkData aMark;
3547 1 : aMark.SetMarkArea(aDestRange);
3548 :
3549 : // Put some texts in B1:B5.
3550 1 : m_pDoc->SetString(ScAddress(1,0,0), "A");
3551 1 : m_pDoc->SetString(ScAddress(1,1,0), "B");
3552 1 : m_pDoc->SetString(ScAddress(1,2,0), "C");
3553 1 : m_pDoc->SetString(ScAddress(1,3,0), "D");
3554 1 : m_pDoc->SetString(ScAddress(1,4,0), "E");
3555 :
3556 : // Set the background color of B1:B5 to blue.
3557 2 : ScPatternAttr aCellBackColor(m_pDoc->GetPool());
3558 1 : aCellBackColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
3559 1 : m_pDoc->ApplyPatternAreaTab(1, 0, 1, 4, 0, aCellBackColor);
3560 :
3561 : // Insert notes to B1:B5.
3562 1 : m_pDoc->GetOrCreateNote(ScAddress(1,0,0));
3563 1 : m_pDoc->GetOrCreateNote(ScAddress(1,1,0));
3564 1 : m_pDoc->GetOrCreateNote(ScAddress(1,2,0));
3565 1 : m_pDoc->GetOrCreateNote(ScAddress(1,3,0));
3566 1 : m_pDoc->GetOrCreateNote(ScAddress(1,4,0));
3567 :
3568 : // Prepare a clipboard content interleaved with empty cells.
3569 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
3570 1 : aClipDoc.ResetClip(m_pDoc, &aMark);
3571 2 : ScClipParam aParam(aSrcRange, false);
3572 1 : aClipDoc.SetClipParam(aParam);
3573 1 : aClipDoc.SetString(ScAddress(0,0,0), "Clip1");
3574 1 : aClipDoc.SetString(ScAddress(0,2,0), "Clip2");
3575 1 : aClipDoc.SetString(ScAddress(0,4,0), "Clip3");
3576 :
3577 : // Set the background color of A1:A5 to yellow.
3578 1 : aCellBackColor.GetItemSet().Put(SvxBrushItem(COL_YELLOW, ATTR_BACKGROUND));
3579 1 : aClipDoc.ApplyPatternAreaTab(0, 0, 0, 4, 0, aCellBackColor);
3580 :
3581 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, aClipDoc.GetCellType(ScAddress(0,0,0)));
3582 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, aClipDoc.GetCellType(ScAddress(0,1,0)));
3583 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, aClipDoc.GetCellType(ScAddress(0,2,0)));
3584 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, aClipDoc.GetCellType(ScAddress(0,3,0)));
3585 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, aClipDoc.GetCellType(ScAddress(0,4,0)));
3586 :
3587 : // Check the initial condition.
3588 : {
3589 : Check aChecks[] = {
3590 : { "A", COL_BLUE, true },
3591 : { "B", COL_BLUE, true },
3592 : { "C", COL_BLUE, true },
3593 : { "D", COL_BLUE, true },
3594 : { "E", COL_BLUE, true },
3595 1 : };
3596 :
3597 1 : bool bRes = aTest.checkRange(ScAddress(1,0,0), aChecks, aChecks + SAL_N_ELEMENTS(aChecks));
3598 1 : CPPUNIT_ASSERT_MESSAGE("Initial check failed.", bRes);
3599 : }
3600 :
3601 : // Create undo document.
3602 1 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
3603 1 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
3604 1 : m_pDoc->CopyToDocument(aDestRange, IDF_ALL, false, pUndoDoc, &aMark);
3605 :
3606 : // Paste clipboard content onto A1:A5 but skip empty cells.
3607 1 : bool bSkipEmpty = true;
3608 1 : m_pDoc->CopyFromClip(aDestRange, aMark, IDF_ALL, pUndoDoc, &aClipDoc, true, false, false, bSkipEmpty);
3609 :
3610 : // Create redo document.
3611 1 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
3612 1 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
3613 1 : m_pDoc->CopyToDocument(aDestRange, IDF_ALL, false, pRedoDoc, &aMark);
3614 :
3615 : // Create an undo object for this.
3616 1 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
3617 2 : ScUndoPaste aUndo(&getDocShell(), aDestRange, aMark, pUndoDoc, pRedoDoc, IDF_ALL, pRefUndoData);
3618 :
3619 : // Check the content after the paste.
3620 : {
3621 : Check aChecks[] = {
3622 : { "Clip1", COL_YELLOW, false },
3623 : { "B", COL_BLUE, true },
3624 : { "Clip2", COL_YELLOW, false },
3625 : { "D", COL_BLUE, true },
3626 : { "Clip3", COL_YELLOW, false },
3627 1 : };
3628 :
3629 1 : bool bRes = aTest.checkRange(ScAddress(1,0,0), aChecks, aChecks + SAL_N_ELEMENTS(aChecks));
3630 1 : CPPUNIT_ASSERT_MESSAGE("Check after paste failed.", bRes);
3631 : }
3632 :
3633 : // Undo, and check the content.
3634 1 : aUndo.Undo();
3635 : {
3636 : Check aChecks[] = {
3637 : { "A", COL_BLUE, true },
3638 : { "B", COL_BLUE, true },
3639 : { "C", COL_BLUE, true },
3640 : { "D", COL_BLUE, true },
3641 : { "E", COL_BLUE, true },
3642 1 : };
3643 :
3644 1 : bool bRes = aTest.checkRange(ScAddress(1,0,0), aChecks, aChecks + SAL_N_ELEMENTS(aChecks));
3645 1 : CPPUNIT_ASSERT_MESSAGE("Check after undo failed.", bRes);
3646 : }
3647 :
3648 : // Redo, and check the content again.
3649 1 : aUndo.Redo();
3650 : {
3651 : Check aChecks[] = {
3652 : { "Clip1", COL_YELLOW, false },
3653 : { "B", COL_BLUE, true },
3654 : { "Clip2", COL_YELLOW, false },
3655 : { "D", COL_BLUE, true },
3656 : { "Clip3", COL_YELLOW, false },
3657 1 : };
3658 :
3659 1 : bool bRes = aTest.checkRange(ScAddress(1,0,0), aChecks, aChecks + SAL_N_ELEMENTS(aChecks));
3660 1 : CPPUNIT_ASSERT_MESSAGE("Check after redo failed.", bRes);
3661 : }
3662 :
3663 2 : m_pDoc->DeleteTab(0);
3664 1 : }
3665 :
3666 0 : void Test::testCopyPasteSkipEmptyConditionalFormatting()
3667 : {
3668 0 : m_pDoc->InsertTab(0, "Test");
3669 :
3670 0 : ScRange aDestRange(0,0,0,1,2,0);
3671 0 : ScRange aSrcRange(3,3,0,5,4,0);
3672 :
3673 0 : ScMarkData aMark;
3674 0 : aMark.SetMarkArea(aDestRange);
3675 :
3676 0 : m_pDoc->SetValue(0,0,0,1);
3677 0 : m_pDoc->SetValue(1,0,0,1);
3678 0 : m_pDoc->SetValue(0,1,0,1);
3679 0 : m_pDoc->SetValue(0,2,0,1);
3680 0 : m_pDoc->SetValue(1,2,0,1);
3681 :
3682 : //create conditional formatting for A1:B3
3683 0 : ScConditionalFormatList* pCondFormatList = new ScConditionalFormatList();
3684 0 : m_pDoc->SetCondFormList(pCondFormatList, 0);
3685 :
3686 0 : ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
3687 0 : pFormat->AddRange(aDestRange);
3688 0 : sal_uLong nCondFormatKey = m_pDoc->AddCondFormat(pFormat, 0);
3689 :
3690 : // Prepare a clipboard content interleaved with empty cells.
3691 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
3692 0 : aClipDoc.ResetClip(m_pDoc, &aMark);
3693 0 : ScClipParam aParam(aSrcRange, false);
3694 0 : aClipDoc.SetClipParam(aParam);
3695 0 : aClipDoc.SetValue(3,3,0,2);
3696 0 : aClipDoc.SetValue(4,3,0,2);
3697 0 : aClipDoc.SetValue(4,4,0,2);
3698 0 : aClipDoc.SetValue(3,5,0,2);
3699 0 : aClipDoc.SetValue(4,5,0,2);
3700 :
3701 0 : ScConditionalFormat* pClipFormat = new ScConditionalFormat(2, &aClipDoc);
3702 0 : pClipFormat->AddRange(aSrcRange);
3703 0 : aClipDoc.AddCondFormat(pClipFormat, 0);
3704 :
3705 : // Create undo document.
3706 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
3707 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
3708 0 : m_pDoc->CopyToDocument(aDestRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
3709 :
3710 : // Paste clipboard content onto A1:A5 but skip empty cells.
3711 0 : bool bSkipEmpty = true;
3712 0 : m_pDoc->CopyFromClip(aDestRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc, true, false, false, bSkipEmpty);
3713 :
3714 0 : ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
3715 0 : CPPUNIT_ASSERT_EQUAL(size_t(2), pList->size());
3716 0 : CPPUNIT_ASSERT(m_pDoc->GetCondFormat(1,1,0));
3717 : // empty cell in copy area does not overwrite conditional formatting
3718 0 : CPPUNIT_ASSERT_EQUAL(sal_uInt32(nCondFormatKey), m_pDoc->GetCondFormat(1,1,0)->GetKey());
3719 0 : for(SCCOL nCol = 0; nCol <= 1; ++nCol)
3720 : {
3721 0 : for(SCROW nRow = 0; nRow <= 2; ++nRow)
3722 : {
3723 0 : if(nRow == 1 && nCol == 1)
3724 0 : continue;
3725 :
3726 0 : CPPUNIT_ASSERT(m_pDoc->GetCondFormat(nCol, nRow, 0));
3727 0 : CPPUNIT_ASSERT(nCondFormatKey != m_pDoc->GetCondFormat(nCol, nRow, 0)->GetKey());
3728 : }
3729 : }
3730 0 : m_pDoc->DeleteTab(0);
3731 0 : }
3732 :
3733 1 : void Test::testUndoCut()
3734 : {
3735 1 : m_pDoc->InsertTab(0, "Test");
3736 :
3737 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
3738 :
3739 : // Insert values into A1:A3.
3740 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3741 1 : m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
3742 1 : m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
3743 :
3744 : // SUM in A4.
3745 1 : m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
3746 1 : CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(0,3,0));
3747 :
3748 : // Select A1:A3.
3749 2 : ScMarkData aMark;
3750 1 : ScRange aRange(0,0,0,0,2,0);
3751 1 : aMark.SetMarkArea(aRange);
3752 1 : aMark.MarkToMulti();
3753 :
3754 : // Set up an undo object for cutting A1:A3.
3755 1 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
3756 1 : pUndoDoc->InitUndo(m_pDoc, 0 ,0);
3757 1 : m_pDoc->CopyToDocument(aRange, IDF_ALL, false, pUndoDoc);
3758 1 : CPPUNIT_ASSERT_EQUAL( 1.0, pUndoDoc->GetValue(ScAddress(0,0,0)));
3759 1 : CPPUNIT_ASSERT_EQUAL( 10.0, pUndoDoc->GetValue(ScAddress(0,1,0)));
3760 1 : CPPUNIT_ASSERT_EQUAL(100.0, pUndoDoc->GetValue(ScAddress(0,2,0)));
3761 2 : ScUndoCut aUndo(&getDocShell(), aRange, aRange.aEnd, aMark, pUndoDoc);
3762 :
3763 : // "Cut" the selection.
3764 1 : m_pDoc->DeleteSelection(IDF_ALL, aMark);
3765 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(0,3,0)); // The SUM should be zero after the "cut".
3766 :
3767 : // Undo it, and check the result.
3768 1 : aUndo.Undo();
3769 1 : CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
3770 1 : CPPUNIT_ASSERT_EQUAL( 10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
3771 1 : CPPUNIT_ASSERT_EQUAL(100.0, m_pDoc->GetValue(ScAddress(0,2,0)));
3772 1 : CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(0,3,0)); // The SUM value should be back to the original.
3773 :
3774 : // Redo it and check.
3775 1 : aUndo.Redo();
3776 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(0,3,0));
3777 :
3778 : // Undo again.
3779 1 : aUndo.Undo();
3780 1 : CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(0,3,0));
3781 :
3782 2 : m_pDoc->DeleteTab(0);
3783 1 : }
3784 :
3785 1 : void Test::testMoveBlock()
3786 : {
3787 1 : m_pDoc->InsertTab(0, "SheetNotes");
3788 :
3789 1 : m_pDoc->SetValue(0, 0, 0, 1);
3790 1 : m_pDoc->SetString(1, 0, 0, "=A1+1");
3791 1 : m_pDoc->SetString(2, 0, 0, "test");
3792 :
3793 : // add notes to A1:C1
3794 1 : ScAddress aAddrA1 (0, 0, 0);
3795 1 : OUString aHelloA1("Hello world in A1");
3796 1 : ScPostIt* pNoteA1 = m_pDoc->GetOrCreateNote(aAddrA1);
3797 1 : pNoteA1->SetText(aAddrA1, aHelloA1);
3798 1 : ScAddress aAddrB1 (1, 0, 0);
3799 2 : OUString aHelloB1("Hello world in B1");
3800 1 : ScPostIt* pNoteB1 = m_pDoc->GetOrCreateNote(aAddrB1);
3801 1 : pNoteB1->SetText(aAddrB1, aHelloB1);
3802 1 : ScAddress aAddrC1 (2, 0, 0);
3803 2 : OUString aHelloC1("Hello world in C1");
3804 1 : ScPostIt* pNoteC1 = m_pDoc->GetOrCreateNote(aAddrC1);
3805 1 : pNoteC1->SetText(aAddrC1, aHelloC1);
3806 1 : ScAddress aAddrD1 (3, 0, 0);
3807 :
3808 : // previous tests on cell note content are ok. this one fails !!! :(
3809 : //CPPUNIT_ASSERT_MESSAGE("Note content in B1 before move block", m_pDoc->GetNote(aAddrB1)->GetText() == aHelloB1);
3810 :
3811 : // move notes to B1:D1
3812 1 : bool bCut = true;
3813 1 : ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
3814 1 : bool bMoveDone = rDocFunc.MoveBlock(ScRange(0, 0 ,0 ,2 ,0 ,0), ScAddress(1, 0, 0), bCut, false, false, false);
3815 :
3816 1 : CPPUNIT_ASSERT_MESSAGE("Cells not moved", bMoveDone);
3817 :
3818 : //check cell content
3819 2 : OUString aString = m_pDoc->GetString(3, 0, 0);
3820 1 : CPPUNIT_ASSERT_MESSAGE("Cell D1 should contain: test", aString == "test");
3821 1 : m_pDoc->GetFormula(2, 0, 0, aString);
3822 1 : CPPUNIT_ASSERT_MESSAGE("Cell C1 should contain an updated formula", aString == "=B1+1");
3823 1 : double fValue = m_pDoc->GetValue(aAddrB1);
3824 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("Cell B1 should contain 1", fValue, 1);
3825 :
3826 : // cell notes has been moved 1 cell right (event when overlapping)
3827 1 : CPPUNIT_ASSERT_MESSAGE("There should be NO note on A1", !m_pDoc->HasNote(aAddrA1));
3828 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on B1", m_pDoc->HasNote(aAddrB1));
3829 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on C1", m_pDoc->HasNote(aAddrC1));
3830 1 : CPPUNIT_ASSERT_MESSAGE("There should be a note on D1", m_pDoc->HasNote(aAddrD1));
3831 : /* still failing, wrong content ???
3832 : OUString sNoteText;
3833 : sNoteText = m_pDoc->GetNote(aAddrB1)->GetText();
3834 : CPPUNIT_ASSERT_MESSAGE("Note content in B1", sNoteText == aHelloA1);
3835 : sNoteText = m_pDoc->GetNote(aAddrC1)->GetText();
3836 : CPPUNIT_ASSERT_MESSAGE("Note content in C1", sNoteText == aHelloB1);
3837 : sNoteText = m_pDoc->GetNote(aAddrD1)->GetText();
3838 : CPPUNIT_ASSERT_MESSAGE("Note content in D1", sNoteText == aHelloC1);
3839 : */
3840 :
3841 2 : m_pDoc->DeleteTab(0);
3842 1 : }
3843 :
3844 1 : void Test::testCopyPasteRelativeFormula()
3845 : {
3846 1 : m_pDoc->InsertTab(0, "Formula");
3847 :
3848 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
3849 :
3850 : // Insert values to A2 and A4.
3851 1 : m_pDoc->SetValue(ScAddress(0,1,0), 1);
3852 1 : m_pDoc->SetValue(ScAddress(0,3,0), 2);
3853 :
3854 : // Insert formula to B4.
3855 1 : m_pDoc->SetString(ScAddress(1,3,0), "=A4");
3856 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,3,0)));
3857 :
3858 : // Select and copy B3:B4 to the clipboard.
3859 1 : ScRange aRange(1,2,0,1,3,0);
3860 2 : ScClipParam aClipParam(aRange, false);
3861 2 : ScMarkData aMark;
3862 1 : aMark.SetMarkArea(aRange);
3863 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
3864 1 : m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
3865 :
3866 : // Paste it to B1:B2.
3867 1 : sal_uInt16 nFlags = IDF_ALL;
3868 1 : ScRange aDestRange(1,0,0,1,1,0);
3869 1 : aMark.SetMarkArea(aDestRange);
3870 1 : m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, NULL, &aClipDoc);
3871 :
3872 : // B2 references A2, so the value should be 1.
3873 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,0)));
3874 :
3875 : // Clear content and start over.
3876 1 : clearSheet(m_pDoc, 0);
3877 1 : clearSheet(&aClipDoc, 0);
3878 :
3879 : // Insert a single formula cell in A1.
3880 1 : m_pDoc->SetString(ScAddress(0,0,0), "=ROW()");
3881 1 : const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,0,0));
3882 1 : CPPUNIT_ASSERT(pFC);
3883 1 : CPPUNIT_ASSERT(!pFC->IsShared()); // single formula cell is never shared.
3884 :
3885 : // Copy A1 to clipboard.
3886 1 : aClipParam = ScClipParam(ScAddress(0,0,0), false);
3887 1 : m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
3888 :
3889 1 : pFC = aClipDoc.GetFormulaCell(ScAddress(0,0,0));
3890 1 : CPPUNIT_ASSERT(pFC);
3891 1 : CPPUNIT_ASSERT(!pFC->IsShared());
3892 :
3893 : // Paste to A3.
3894 1 : aDestRange = ScRange(0,2,0,0,2,0);
3895 1 : aMark.SetMarkArea(aDestRange);
3896 1 : m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, NULL, &aClipDoc);
3897 :
3898 1 : pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
3899 1 : CPPUNIT_ASSERT(pFC);
3900 1 : CPPUNIT_ASSERT(!pFC->IsShared());
3901 :
3902 : // Delete A3 and make sure it doesn't crash (see fdo#76132).
3903 1 : clearRange(m_pDoc, ScAddress(0,2,0));
3904 1 : CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(0,2,0)) == CELLTYPE_NONE);
3905 :
3906 2 : m_pDoc->DeleteTab(0);
3907 1 : }
3908 :
3909 1 : void Test::testMergedCells()
3910 : {
3911 : //test merge and unmerge
3912 : //TODO: an undo/redo test for this would be a good idea
3913 1 : m_pDoc->InsertTab(0, "Sheet1");
3914 1 : m_pDoc->DoMerge(0, 1, 1, 3, 3, false);
3915 1 : SCCOL nEndCol = 1;
3916 1 : SCROW nEndRow = 1;
3917 1 : m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0, false);
3918 1 : CPPUNIT_ASSERT_MESSAGE("did not merge cells", nEndCol == 3 && nEndRow == 3);
3919 1 : ScRange aRange(0,2,0,MAXCOL,2,0);
3920 1 : ScMarkData aMark;
3921 1 : aMark.SetMarkArea(aRange);
3922 1 : getDocShell().GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS, true, true);
3923 1 : m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0, false);
3924 1 : CPPUNIT_ASSERT_MESSAGE("did not increase merge area", nEndCol == 3 && nEndRow == 4);
3925 1 : m_pDoc->DeleteTab(0);
3926 1 : }
3927 :
3928 :
3929 1 : void Test::testRenameTable()
3930 : {
3931 : //test set rename table
3932 : //TODO: set name1 and name2 and do an undo to check if name 1 is set now
3933 : //TODO: also check if new name for table is same as another table
3934 :
3935 1 : m_pDoc->InsertTab(0, "Sheet1");
3936 1 : m_pDoc->InsertTab(1, "Sheet2");
3937 :
3938 : //test case 1 , rename table2 to sheet 1, it should return error
3939 1 : OUString nameToSet = "Sheet1";
3940 1 : ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
3941 1 : CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
3942 :
3943 : //test case 2 , simple rename to check name
3944 1 : nameToSet = "test1";
3945 1 : getDocShell().GetDocFunc().RenameTable(0,nameToSet,false,true);
3946 2 : OUString nameJustSet;
3947 1 : m_pDoc->GetName(0,nameJustSet);
3948 1 : CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
3949 :
3950 : //test case 3 , rename again
3951 2 : OUString anOldName;
3952 1 : m_pDoc->GetName(0,anOldName);
3953 :
3954 1 : nameToSet = "test2";
3955 1 : rDocFunc.RenameTable(0,nameToSet,false,true);
3956 1 : m_pDoc->GetName(0,nameJustSet);
3957 1 : CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
3958 :
3959 : //test case 4 , check if undo works
3960 1 : SfxUndoAction* pUndo = new ScUndoRenameTab(&getDocShell(),0,anOldName,nameToSet);
3961 1 : pUndo->Undo();
3962 1 : m_pDoc->GetName(0,nameJustSet);
3963 1 : CPPUNIT_ASSERT_MESSAGE("the correct name is not set after undo", nameJustSet == anOldName);
3964 :
3965 1 : pUndo->Redo();
3966 1 : m_pDoc->GetName(0,nameJustSet);
3967 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after redo", nameJustSet == nameToSet);
3968 :
3969 1 : m_pDoc->DeleteTab(0);
3970 2 : m_pDoc->DeleteTab(1);
3971 1 : }
3972 :
3973 1 : void Test::testSetBackgroundColor()
3974 : {
3975 : //test set background color
3976 : //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
3977 :
3978 1 : m_pDoc->InsertTab(0, "Sheet1");
3979 1 : Color aColor;
3980 :
3981 : //test yellow
3982 1 : aColor=Color(COL_YELLOW);
3983 1 : getDocShell().GetDocFunc().SetTabBgColor(0,aColor,false, true);
3984 2 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set",
3985 1 : m_pDoc->GetTabBgColor(0) == aColor);
3986 :
3987 :
3988 1 : Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
3989 1 : aColor.SetColor(COL_BLUE);//set BLUE
3990 1 : getDocShell().GetDocFunc().SetTabBgColor(0,aColor,false, true);
3991 2 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set the second time",
3992 1 : m_pDoc->GetTabBgColor(0) == aColor);
3993 :
3994 : //now check for undo
3995 1 : SfxUndoAction* pUndo = new ScUndoTabColor(&getDocShell(), 0, aOldTabBgColor, aColor);
3996 1 : pUndo->Undo();
3997 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aOldTabBgColor);
3998 1 : pUndo->Redo();
3999 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aColor);
4000 1 : m_pDoc->DeleteTab(0);
4001 1 : }
4002 :
4003 1 : void Test::testUpdateReference()
4004 : {
4005 : //test that formulas are correctly updated during sheet delete
4006 : //TODO: add tests for relative references, updating of named ranges, ...
4007 1 : m_pDoc->InsertTab(0, "Sheet1");
4008 1 : m_pDoc->InsertTab(1, "Sheet2");
4009 1 : m_pDoc->InsertTab(2, "Sheet3");
4010 1 : m_pDoc->InsertTab(3, "Sheet4");
4011 :
4012 1 : m_pDoc->SetValue(0,0,2, 1);
4013 1 : m_pDoc->SetValue(1,0,2, 2);
4014 1 : m_pDoc->SetValue(1,1,3, 4);
4015 1 : m_pDoc->SetString(2,0,2, "=A1+B1");
4016 1 : m_pDoc->SetString(2,1,2, "=Sheet4.B2+A1");
4017 :
4018 : double aValue;
4019 1 : m_pDoc->GetValue(2,0,2, aValue);
4020 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 3);
4021 1 : m_pDoc->GetValue(2,1,2, aValue);
4022 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 5);
4023 :
4024 : //test deleting both sheets: one is not directly before the sheet, the other one is
4025 1 : m_pDoc->DeleteTab(0);
4026 1 : m_pDoc->GetValue(2,0,1, aValue);
4027 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 3);
4028 1 : m_pDoc->GetValue(2,1,1, aValue);
4029 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 5);
4030 :
4031 1 : m_pDoc->DeleteTab(0);
4032 1 : m_pDoc->GetValue(2,0,0, aValue);
4033 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 3);
4034 1 : m_pDoc->GetValue(2,1,0, aValue);
4035 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 5);
4036 :
4037 : //test adding two sheets
4038 1 : m_pDoc->InsertTab(0, "Sheet2");
4039 1 : m_pDoc->GetValue(2,0,1, aValue);
4040 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 3);
4041 1 : m_pDoc->GetValue(2,1,1, aValue);
4042 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 5);
4043 :
4044 1 : m_pDoc->InsertTab(0, "Sheet1");
4045 1 : m_pDoc->GetValue(2,0,2, aValue);
4046 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 3);
4047 1 : m_pDoc->GetValue(2,1,2, aValue);
4048 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 5);
4049 :
4050 : //test new DeleteTabs/InsertTabs methods
4051 1 : m_pDoc->DeleteTabs(0, 2);
4052 1 : m_pDoc->GetValue(2, 0, 0, aValue);
4053 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 3);
4054 1 : m_pDoc->GetValue(2, 1, 0, aValue);
4055 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 5);
4056 :
4057 1 : std::vector<OUString> aSheets;
4058 1 : aSheets.push_back("Sheet1");
4059 1 : aSheets.push_back("Sheet2");
4060 1 : m_pDoc->InsertTabs(0, aSheets, false, true);
4061 1 : m_pDoc->GetValue(2, 0, 2, aValue);
4062 2 : OUString aFormula;
4063 1 : m_pDoc->GetFormula(2,0,2, aFormula);
4064 :
4065 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 3);
4066 1 : m_pDoc->GetValue(2, 1, 2, aValue);
4067 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 5);
4068 :
4069 1 : m_pDoc->DeleteTab(3);
4070 1 : m_pDoc->DeleteTab(2);
4071 1 : m_pDoc->DeleteTab(1);
4072 2 : m_pDoc->DeleteTab(0);
4073 1 : }
4074 :
4075 1 : void Test::testSearchCells()
4076 : {
4077 1 : m_pDoc->InsertTab(0, "Test");
4078 :
4079 1 : m_pDoc->SetString(ScAddress(0,0,0), "A");
4080 1 : m_pDoc->SetString(ScAddress(0,1,0), "B");
4081 1 : m_pDoc->SetString(ScAddress(0,2,0), "A");
4082 : // Leave A4 blank.
4083 1 : m_pDoc->SetString(ScAddress(0,4,0), "A");
4084 1 : m_pDoc->SetString(ScAddress(0,5,0), "B");
4085 1 : m_pDoc->SetString(ScAddress(0,6,0), "C");
4086 :
4087 1 : SvxSearchItem aItem(SID_SEARCH_ITEM);
4088 1 : aItem.SetSearchString(OUString("A"));
4089 1 : aItem.SetCommand(SVX_SEARCHCMD_FIND_ALL);
4090 2 : ScMarkData aMarkData;
4091 1 : aMarkData.SelectOneTable(0);
4092 1 : SCCOL nCol = 0;
4093 1 : SCROW nRow = 0;
4094 1 : SCTAB nTab = 0;
4095 2 : ScRangeList aMatchedRanges;
4096 2 : OUString aUndoStr;
4097 1 : bool bSuccess = m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr);
4098 :
4099 1 : CPPUNIT_ASSERT_MESSAGE("Search And Replace should succeed", bSuccess);
4100 1 : CPPUNIT_ASSERT_MESSAGE("There should be exactly 3 matching cells.", aMatchedRanges.size() == 3);
4101 1 : ScAddress aHit(0,0,0);
4102 1 : CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.In(aHit));
4103 1 : aHit.SetRow(2);
4104 1 : CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.In(aHit));
4105 1 : aHit.SetRow(4);
4106 1 : CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.In(aHit));
4107 :
4108 2 : m_pDoc->DeleteTab(0);
4109 1 : }
4110 :
4111 1 : void Test::testFormulaPosition()
4112 : {
4113 1 : m_pDoc->InsertTab(0, "Test");
4114 :
4115 1 : ScAddress aPos(0,0,0); // A1
4116 1 : m_pDoc->SetString(aPos, "=ROW()");
4117 1 : aPos.IncRow(); // A2
4118 1 : m_pDoc->SetString(aPos, "=ROW()");
4119 1 : aPos.SetRow(3); // A4;
4120 1 : m_pDoc->SetString(aPos, "=ROW()");
4121 :
4122 : {
4123 1 : SCROW aRows[] = { 0, 1, 3 };
4124 1 : bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4125 1 : CPPUNIT_ASSERT(bRes);
4126 : }
4127 :
4128 1 : m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
4129 : {
4130 1 : SCROW aRows[] = { 0, 6, 8 };
4131 1 : bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4132 1 : CPPUNIT_ASSERT(bRes);
4133 : }
4134 :
4135 1 : m_pDoc->DeleteTab(0);
4136 1 : }
4137 :
4138 : namespace {
4139 :
4140 4 : bool hasRange(const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange, const ScAddress& rPos)
4141 : {
4142 4 : std::vector<ScTokenRef>::const_iterator it = rRefTokens.begin(), itEnd = rRefTokens.end();
4143 5 : for (; it != itEnd; ++it)
4144 : {
4145 5 : const ScTokenRef& p = *it;
4146 5 : if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
4147 0 : continue;
4148 :
4149 5 : switch (p->GetType())
4150 : {
4151 : case formula::svSingleRef:
4152 : {
4153 2 : ScSingleRefData aData = p->GetSingleRef();
4154 2 : if (rRange.aStart != rRange.aEnd)
4155 0 : break;
4156 :
4157 2 : ScAddress aThis = aData.toAbs(rPos);
4158 2 : if (aThis == rRange.aStart)
4159 2 : return true;
4160 : }
4161 0 : break;
4162 : case formula::svDoubleRef:
4163 : {
4164 3 : ScComplexRefData aData = p->GetDoubleRef();
4165 3 : ScRange aThis = aData.toAbs(rPos);
4166 3 : if (aThis == rRange)
4167 2 : return true;
4168 : }
4169 1 : break;
4170 : default:
4171 : ;
4172 : }
4173 : }
4174 0 : return false;
4175 : }
4176 :
4177 : }
4178 :
4179 1 : void Test::testJumpToPrecedentsDependents()
4180 : {
4181 : // Precedent is another cell that the cell references, while dependent is
4182 : // another cell that references it.
4183 1 : m_pDoc->InsertTab(0, "Test");
4184 :
4185 1 : m_pDoc->SetString(2, 0, 0, "=A1+A2+B3"); // C1
4186 1 : m_pDoc->SetString(2, 1, 0, "=A1"); // C2
4187 1 : m_pDoc->CalcAll();
4188 :
4189 1 : std::vector<ScTokenRef> aRefTokens;
4190 1 : ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
4191 :
4192 : {
4193 : // C1's precedent should be A1:A2,B3.
4194 1 : ScAddress aC1(2, 0, 0);
4195 1 : ScRangeList aRange(aC1);
4196 1 : rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4197 2 : CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
4198 1 : hasRange(aRefTokens, ScRange(0, 0, 0, 0, 1, 0), aC1));
4199 2 : CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
4200 2 : hasRange(aRefTokens, ScRange(1, 2, 0), aC1));
4201 : }
4202 :
4203 : {
4204 : // C2's precedent should be A1 only.
4205 1 : ScAddress aC2(2, 1, 0);
4206 1 : ScRangeList aRange(aC2);
4207 1 : rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4208 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
4209 1 : aRefTokens.size(), static_cast<size_t>(1));
4210 2 : CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
4211 2 : hasRange(aRefTokens, ScRange(0, 0, 0), aC2));
4212 : }
4213 :
4214 : {
4215 : // A1's dependent should be C1:C2.
4216 1 : ScAddress aA1(0, 0, 0);
4217 1 : ScRangeList aRange(aA1);
4218 1 : rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
4219 2 : CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
4220 2 : aRefTokens.size() == 1 && hasRange(aRefTokens, ScRange(2, 0, 0, 2, 1, 0), aA1));
4221 : }
4222 :
4223 1 : m_pDoc->DeleteTab(0);
4224 1 : }
4225 :
4226 1 : void Test::testAutoFill()
4227 : {
4228 1 : m_pDoc->InsertTab(0, "test");
4229 :
4230 1 : m_pDoc->SetValue(0,0,0,1);
4231 :
4232 1 : ScMarkData aMarkData;
4233 1 : aMarkData.SelectTable(0, true);
4234 :
4235 1 : m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 5);
4236 7 : for (SCROW i = 0; i< 6; ++i)
4237 6 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
4238 :
4239 : // check that hidden rows are not affected by autofill
4240 : // set values for hidden rows
4241 1 : m_pDoc->SetValue(0,1,0,10);
4242 1 : m_pDoc->SetValue(0,2,0,10);
4243 :
4244 1 : m_pDoc->SetRowHidden(1, 2, 0, true);
4245 1 : m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 8);
4246 :
4247 1 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
4248 1 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
4249 6 : for (SCROW i = 3; i< 8; ++i)
4250 5 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
4251 :
4252 1 : m_pDoc->Fill( 0, 0, 0, 8, NULL, aMarkData, 5, FILL_TO_RIGHT );
4253 6 : for (SCCOL i = 0; i < 5; ++i)
4254 : {
4255 45 : for(SCROW j = 0; j < 8; ++j)
4256 : {
4257 40 : if (j > 2)
4258 : {
4259 25 : ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
4260 : }
4261 15 : else if (j == 0)
4262 : {
4263 5 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
4264 : }
4265 10 : else if (j == 1 || j== 2)
4266 : {
4267 10 : if(i == 0)
4268 2 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
4269 : else
4270 8 : ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
4271 : }
4272 : }
4273 : }
4274 :
4275 : // test auto fill user data lists
4276 1 : m_pDoc->SetString( 0, 100, 0, "January" );
4277 1 : m_pDoc->Fill( 0, 100, 0, 100, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4278 2 : OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
4279 1 : CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("February") );
4280 1 : aTestValue = m_pDoc->GetString( 0, 102, 0 );
4281 1 : CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("March") );
4282 :
4283 : // test that two same user data list entries will not result in incremental fill
4284 1 : m_pDoc->SetString( 0, 101, 0, "January" );
4285 1 : m_pDoc->Fill( 0, 100, 0, 101, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4286 3 : for ( SCROW i = 102; i <= 103; ++i )
4287 : {
4288 2 : aTestValue = m_pDoc->GetString( 0, i, 0 );
4289 2 : CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("January") );
4290 : }
4291 :
4292 : // Clear column A for a new test.
4293 1 : clearRange(m_pDoc, ScRange(0,0,0,0,MAXROW,0));
4294 1 : m_pDoc->SetRowHidden(0, MAXROW, 0, false); // Show all rows.
4295 :
4296 : // Fill A1:A6 with 1,2,3,4,5,6.
4297 1 : ScDocFunc& rFunc = getDocShell().GetDocFunc();
4298 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4299 1 : ScRange aRange(0,0,0,0,5,0);
4300 1 : aMarkData.SetMarkArea(aRange);
4301 1 : rFunc.FillSeries(aRange, &aMarkData, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, MAXDOUBLE, 1.0, MAXDOUBLE, true, true);
4302 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4303 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4304 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4305 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4306 1 : CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4307 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4308 :
4309 : // Undo should clear the area except for the top cell.
4310 1 : SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4311 1 : CPPUNIT_ASSERT(pUndoMgr);
4312 1 : pUndoMgr->Undo();
4313 :
4314 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4315 6 : for (SCROW i = 1; i <= 5; ++i)
4316 5 : CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(0,i,0)) == CELLTYPE_NONE);
4317 :
4318 : // Redo should put the serial values back in.
4319 1 : pUndoMgr->Redo();
4320 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4321 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4322 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4323 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4324 1 : CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4325 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4326 :
4327 2 : m_pDoc->DeleteTab(0);
4328 1 : }
4329 :
4330 1 : void Test::testCopyPasteFormulas()
4331 : {
4332 1 : m_pDoc->InsertTab(0, "Sheet1");
4333 1 : m_pDoc->InsertTab(1, "Sheet2");
4334 :
4335 1 : m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
4336 1 : m_pDoc->SetString(0,1,0, "=$A$1+B2" );
4337 1 : m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
4338 1 : m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
4339 1 : m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
4340 :
4341 : // to prevent ScEditableTester in ScDocFunc::MoveBlock
4342 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,0,0), 1.0);
4343 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,1,0), 1.0);
4344 1 : ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
4345 1 : bool bMoveDone = rDocFunc.MoveBlock(ScRange(0,0,0,0,4,0), ScAddress( 10, 10, 0), false, false, false, true);
4346 :
4347 : // check that moving was succesful, mainly for editable tester
4348 1 : CPPUNIT_ASSERT(bMoveDone);
4349 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,10,0), 1.0);
4350 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,11,0), 1.0);
4351 1 : OUString aFormula;
4352 1 : m_pDoc->GetFormula(10,10,0, aFormula);
4353 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($A$1)"));
4354 1 : m_pDoc->GetFormula(10,11,0, aFormula);
4355 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$A$1+L12"));
4356 1 : m_pDoc->GetFormula(10,12,0, aFormula);
4357 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K11"));
4358 1 : m_pDoc->GetFormula(10,13,0, aFormula);
4359 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.$A$1"));
4360 1 : m_pDoc->GetFormula(10,14,0, aFormula);
4361 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K$1"));
4362 1 : }
4363 :
4364 1 : void Test::testCopyPasteFormulasExternalDoc()
4365 : {
4366 1 : OUString aDocName("file:///source.fake");
4367 1 : SfxMedium* pMedium = new SfxMedium(aDocName, STREAM_STD_READWRITE);
4368 1 : getDocShell().DoInitNew(pMedium);
4369 1 : m_pDoc = getDocShell().GetDocument();
4370 :
4371 2 : ScDocShellRef xExtDocSh = new ScDocShell;
4372 2 : OUString aExtDocName("file:///extdata.fake");
4373 2 : OUString aExtSh1Name("ExtSheet1");
4374 2 : OUString aExtSh2Name("ExtSheet2");
4375 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
4376 1 : xExtDocSh->DoInitNew(pMed);
4377 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
4378 1 : findLoadedDocShellByName(aExtDocName) != NULL);
4379 :
4380 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
4381 1 : pExtDoc->InsertTab(0, aExtSh1Name);
4382 1 : pExtDoc->InsertTab(1, aExtSh2Name);
4383 :
4384 1 : m_pDoc->InsertTab(0, "Sheet1");
4385 1 : m_pDoc->InsertTab(1, "Sheet2");
4386 :
4387 1 : m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
4388 1 : m_pDoc->SetString(0,1,0, "=$A$1+B2" );
4389 1 : m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
4390 1 : m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
4391 1 : m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
4392 1 : m_pDoc->SetString(0,5,0, "=$Sheet1.$A$1");
4393 :
4394 1 : ScRange aRange(0,0,0,0,5,0);
4395 2 : ScClipParam aClipParam(aRange, false);
4396 2 : ScMarkData aMark;
4397 1 : aMark.SetMarkArea(aRange);
4398 1 : ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
4399 1 : m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
4400 :
4401 1 : sal_uInt16 nFlags = IDF_ALL;
4402 1 : aRange = ScRange(1,1,1,1,6,1);
4403 2 : ScMarkData aMarkData2;
4404 1 : aMarkData2.SetMarkArea(aRange);
4405 1 : pExtDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
4406 :
4407 2 : OUString aFormula;
4408 1 : pExtDoc->GetFormula(1,1,1, aFormula);
4409 : //adjust absolute refs pointing to the copy area
4410 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($B$2)"));
4411 1 : pExtDoc->GetFormula(1,2,1, aFormula);
4412 : //adjust absolute refs and keep relative refs
4413 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$B$2+C3"));
4414 1 : pExtDoc->GetFormula(1,3,1, aFormula);
4415 : // make absolute sheet refs external refs
4416 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B2"));
4417 1 : pExtDoc->GetFormula(1,4,1, aFormula);
4418 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.$A$1"));
4419 1 : pExtDoc->GetFormula(1,5,1, aFormula);
4420 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B$1"));
4421 1 : pExtDoc->GetFormula(1,6,1, aFormula);
4422 2 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$ExtSheet2.$B$2"));
4423 1 : }
4424 :
4425 1 : void Test::testFindAreaPosVertical()
4426 : {
4427 : const char* aData[][3] = {
4428 : { 0, "1", "1" },
4429 : { "1", 0, "1" },
4430 : { "1", "1", "1" },
4431 : { 0, "1", "1" },
4432 : { "1", "1", "1" },
4433 : { "1", 0, "1" },
4434 : { "1", "1", "1" },
4435 1 : };
4436 :
4437 1 : m_pDoc->InsertTab(0, "Test1");
4438 1 : clearRange( m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
4439 1 : ScAddress aPos(0,0,0);
4440 1 : ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4441 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4442 :
4443 1 : m_pDoc->SetRowHidden(4,4,0,true);
4444 1 : bool bHidden = m_pDoc->RowHidden(4,0);
4445 1 : CPPUNIT_ASSERT(bHidden);
4446 :
4447 1 : SCCOL nCol = 0;
4448 1 : SCROW nRow = 0;
4449 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4450 :
4451 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4452 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4453 :
4454 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4455 :
4456 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
4457 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4458 :
4459 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4460 :
4461 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
4462 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4463 :
4464 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4465 :
4466 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4467 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4468 :
4469 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4470 :
4471 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(MAXROW), nRow);
4472 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4473 :
4474 1 : nCol = 1;
4475 1 : nRow = 2;
4476 :
4477 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4478 :
4479 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
4480 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4481 :
4482 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4483 :
4484 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4485 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4486 :
4487 1 : nCol = 2;
4488 1 : nRow = 6;
4489 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
4490 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4491 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4492 :
4493 :
4494 1 : m_pDoc->DeleteTab(0);
4495 1 : }
4496 :
4497 1 : void Test::testFindAreaPosColRight()
4498 : {
4499 : const char* aData[][7] = {
4500 : { "", "1", "1", "", "1", "1", "1" },
4501 1 : { "", "", "1", "1", "1", "", "1" }, };
4502 :
4503 1 : m_pDoc->InsertTab(0, "test1");
4504 1 : clearRange( m_pDoc, ScRange(0, 0, 0, 7, SAL_N_ELEMENTS(aData), 0));
4505 1 : ScAddress aPos(0,0,0);
4506 1 : ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4507 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4508 :
4509 1 : m_pDoc->SetColHidden(4,4,0,true);
4510 1 : bool bHidden = m_pDoc->ColHidden(4,0);
4511 1 : CPPUNIT_ASSERT(bHidden);
4512 :
4513 1 : SCCOL nCol = 0;
4514 1 : SCROW nRow = 0;
4515 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4516 :
4517 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4518 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4519 :
4520 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4521 :
4522 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4523 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4524 :
4525 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4526 :
4527 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4528 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
4529 :
4530 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4531 :
4532 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4533 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4534 :
4535 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4536 :
4537 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4538 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(MAXCOL), nCol);
4539 :
4540 1 : nCol = 2;
4541 1 : nRow = 1;
4542 :
4543 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4544 :
4545 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4546 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
4547 :
4548 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4549 :
4550 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4551 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4552 :
4553 1 : m_pDoc->DeleteTab(0);
4554 1 : }
4555 :
4556 : // regression test fo fdo#53814, sorting doens't work as expected
4557 : // if cells in the sort are referenced by formulas
4558 1 : void Test::testSortWithFormulaRefs()
4559 : {
4560 1 : ScDocument* pDoc = getDocShell().GetDocument();
4561 1 : pDoc->InsertTab(0, "List1");
4562 1 : pDoc->InsertTab(1, "List2");
4563 :
4564 : const char* aFormulaData[6] = {
4565 : "=IF($List1.A2<>\"\",$List1.A2,\"\")",
4566 : "=IF($List1.A3<>\"\",$List1.A3,\"\")",
4567 : "=IF($List1.A4<>\"\",$List1.A4,\"\")",
4568 : "=IF($List1.A5<>\"\",$List1.A5,\"\")",
4569 : "=IF($List1.A6<>\"\",$List1.A6,\"\")",
4570 : "=IF($List1.A7<>\"\",$List1.A7,\"\")",
4571 1 : };
4572 :
4573 : const char* aTextData[4] = {
4574 : "bob",
4575 : "tim",
4576 : "brian",
4577 : "larry",
4578 1 : };
4579 :
4580 : const char* aResults[ 6 ] = {
4581 : "bob",
4582 : "brian",
4583 : "larry",
4584 : "tim",
4585 : "",
4586 : "",
4587 1 : };
4588 : // insert data to sort
4589 1 : SCROW nStart = 1, nEnd = 4;
4590 5 : for ( SCROW i = nStart; i <= nEnd; ++i )
4591 4 : pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
4592 : // insert forumulas
4593 1 : nStart = 0;
4594 1 : nEnd = SAL_N_ELEMENTS(aFormulaData);
4595 7 : for ( SCROW i = nStart; i < nEnd; ++i )
4596 6 : pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
4597 :
4598 1 : ScSortParam aSortData;
4599 1 : aSortData.nCol1 = 0;
4600 1 : aSortData.nCol2 = 0;
4601 1 : aSortData.nRow1 = 1;
4602 1 : aSortData.nRow2 = 7;
4603 1 : aSortData.maKeyState[0].bDoSort = true;
4604 1 : aSortData.maKeyState[0].nField = 0;
4605 :
4606 1 : pDoc->Sort(0, aSortData, false, NULL);
4607 :
4608 1 : nEnd = SAL_N_ELEMENTS( aResults );
4609 7 : for ( SCROW i = nStart; i < nEnd; ++i )
4610 : {
4611 6 : OUString sResult = pDoc->GetString( 0, i + 1, 0);
4612 6 : CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[ i ] ), sResult );
4613 6 : }
4614 1 : pDoc->DeleteTab(0);
4615 1 : pDoc->DeleteTab(1);
4616 1 : }
4617 :
4618 1 : void Test::testSortWithStrings()
4619 : {
4620 1 : m_pDoc->InsertTab(0, "Test");
4621 :
4622 1 : ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
4623 1 : rEE.SetText("Val1");
4624 1 : m_pDoc->SetString(ScAddress(1,1,0), "Header");
4625 1 : m_pDoc->SetString(ScAddress(1,2,0), "Val2");
4626 1 : m_pDoc->SetEditText(ScAddress(1,3,0), rEE.CreateTextObject());
4627 :
4628 1 : CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0)));
4629 1 : CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,2,0)));
4630 1 : CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,3,0)));
4631 :
4632 1 : ScSortParam aParam;
4633 1 : aParam.nCol1 = 1;
4634 1 : aParam.nCol2 = 1;
4635 1 : aParam.nRow1 = 1;
4636 1 : aParam.nRow2 = 3;
4637 1 : aParam.bHasHeader = true;
4638 1 : aParam.maKeyState[0].bDoSort = true;
4639 1 : aParam.maKeyState[0].bAscending = true;
4640 1 : aParam.maKeyState[0].nField = 1;
4641 :
4642 1 : m_pDoc->Sort(0, aParam, false, NULL);
4643 :
4644 1 : CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0)));
4645 1 : CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,2,0)));
4646 1 : CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,3,0)));
4647 :
4648 1 : aParam.maKeyState[0].bAscending = false;
4649 :
4650 1 : m_pDoc->Sort(0, aParam, false, NULL);
4651 :
4652 1 : CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0)));
4653 1 : CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,2,0)));
4654 1 : CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,3,0)));
4655 :
4656 1 : m_pDoc->DeleteTab(0);
4657 1 : }
4658 :
4659 1 : void Test::testSort()
4660 : {
4661 1 : m_pDoc->InsertTab(0, "test1");
4662 :
4663 1 : ScRange aDataRange;
4664 1 : ScAddress aPos(0,0,0);
4665 : {
4666 : const char* aData[][2] = {
4667 : { "2", "4" },
4668 : { "4", "1" },
4669 : { "1", "2" },
4670 : { "1", "23" },
4671 1 : };
4672 :
4673 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
4674 1 : aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4675 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4676 : }
4677 :
4678 : // Insert note in cell B2.
4679 1 : ScAddress rAddr(1, 1, 0);
4680 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
4681 1 : pNote->SetText(rAddr, "Hello");
4682 1 : pNote->SetAuthor("Jim Bob");
4683 :
4684 1 : ScSortParam aSortData;
4685 1 : aSortData.nCol1 = 1;
4686 1 : aSortData.nCol2 = 1;
4687 1 : aSortData.nRow1 = 0;
4688 1 : aSortData.nRow2 = 2;
4689 1 : aSortData.maKeyState[0].bDoSort = true;
4690 1 : aSortData.maKeyState[0].nField = 1;
4691 1 : aSortData.maKeyState[0].bAscending = true;
4692 :
4693 1 : m_pDoc->Sort(0, aSortData, false, NULL);
4694 :
4695 1 : double nVal = m_pDoc->GetValue(1,0,0);
4696 1 : ASSERT_DOUBLES_EQUAL(nVal, 1.0);
4697 :
4698 : // check that note is also moved after sorting
4699 1 : pNote = m_pDoc->GetNote(1, 0, 0);
4700 1 : CPPUNIT_ASSERT(pNote);
4701 :
4702 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
4703 : {
4704 : // 0 = empty cell
4705 : const char* aData[][1] = {
4706 : { "Title" },
4707 : { 0 },
4708 : { 0 },
4709 : { "12" },
4710 : { "b" },
4711 : { "1" },
4712 : { "9" },
4713 : { "123" }
4714 1 : };
4715 :
4716 1 : aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4717 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4718 : }
4719 :
4720 1 : aSortData.nCol1 = aDataRange.aStart.Col();
4721 1 : aSortData.nCol2 = aDataRange.aEnd.Col();
4722 1 : aSortData.nRow1 = aDataRange.aStart.Row();
4723 1 : aSortData.nRow2 = aDataRange.aEnd.Row();
4724 1 : aSortData.bHasHeader = true;
4725 1 : aSortData.maKeyState[0].nField = 0;
4726 1 : m_pDoc->Sort(0, aSortData, false, NULL);
4727 :
4728 : // Title should stay at the top, numbers should be sorted numerically,
4729 : // numbers always come before strings, and empty cells always occur at the
4730 : // end.
4731 1 : CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos));
4732 1 : aPos.IncRow();
4733 1 : CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos));
4734 1 : aPos.IncRow();
4735 1 : CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos));
4736 1 : aPos.IncRow();
4737 1 : CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos));
4738 1 : aPos.IncRow();
4739 1 : CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos));
4740 1 : aPos.IncRow();
4741 1 : CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos));
4742 1 : aPos.IncRow();
4743 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos));
4744 :
4745 1 : m_pDoc->DeleteTab(0);
4746 1 : }
4747 :
4748 1 : void Test::testSortInFormulaGroup()
4749 : {
4750 : static struct {
4751 : SCCOL nCol;
4752 : SCROW nRow;
4753 : const char *pData;
4754 : } aEntries[] = {
4755 : { 0, 0, "3" }, { 1, 0, "=A1" },
4756 : { 0, 1, "1" }, { 1, 1, "=A2" },
4757 : { 0, 2, "20" }, { 1, 2, "=A3" },
4758 : { 0, 3, "10" }, { 1, 3, "=A4+1" }, // swap across groups
4759 : { 0, 4, "2" }, { 1, 4, "=A5+1" },
4760 : { 0, 5, "101" }, { 1, 5, "=A6" }, // swap inside contiguious group
4761 : { 0, 6, "100" }, { 1, 6, "=A7" },
4762 : { 0, 7, "102" }, { 1, 7, "=A8" },
4763 : { 0, 8, "104" }, { 1, 8, "=A9" },
4764 : { 0, 9, "103" }, { 1, 9, "=A10" },
4765 : };
4766 :
4767 1 : m_pDoc->InsertTab(0, "sorttest");
4768 :
4769 21 : for ( SCROW i = 0; i < (SCROW) SAL_N_ELEMENTS( aEntries ); ++i )
4770 : m_pDoc->SetString( aEntries[i].nCol, aEntries[i].nRow, 0,
4771 20 : OUString::createFromAscii( aEntries[i].pData) );
4772 :
4773 1 : ScSortParam aSortData;
4774 1 : aSortData.nCol1 = 0;
4775 1 : aSortData.nCol2 = 1;
4776 1 : aSortData.nRow1 = 0;
4777 1 : aSortData.nRow2 = 9;
4778 1 : aSortData.maKeyState[0].bDoSort = true;
4779 1 : aSortData.maKeyState[0].nField = 0;
4780 1 : aSortData.maKeyState[0].bAscending = true;
4781 :
4782 1 : m_pDoc->Sort(0, aSortData, false, NULL);
4783 :
4784 : static struct {
4785 : SCCOL nCol;
4786 : SCROW nRow;
4787 : double fValue;
4788 : } aResults[] = {
4789 : { 0, 0, 1.0 }, { 1, 0, 1.0 },
4790 : { 0, 1, 2.0 }, { 1, 1, 3.0 },
4791 : { 0, 2, 3.0 }, { 1, 2, 3.0 },
4792 : { 0, 3, 10.0 }, { 1, 3, 11.0 },
4793 : { 0, 4, 20.0 }, { 1, 4, 20.0 },
4794 : { 0, 5, 100.0 }, { 1, 5, 100.0 },
4795 : { 0, 6, 101.0 }, { 1, 6, 101.0 },
4796 : { 0, 7, 102.0 }, { 1, 7, 102.0 },
4797 : { 0, 8, 103.0 }, { 1, 8, 103.0 },
4798 : { 0, 9, 104.0 }, { 1, 9, 104.0 },
4799 : };
4800 :
4801 21 : for ( SCROW i = 0; i < (SCROW) SAL_N_ELEMENTS( aEntries ); ++i )
4802 : {
4803 20 : double val = m_pDoc->GetValue( aEntries[i].nCol, aEntries[i].nRow, 0 );
4804 : // fprintf(stderr, "value at %d %d is %g = %g\n",
4805 : // (int)aResults[i].nRow, (int)aResults[i].nCol,
4806 : // val, aResults[i].fValue);
4807 40 : CPPUNIT_ASSERT_MESSAGE("Mis-matching value after sort.",
4808 20 : rtl::math::approxEqual(val, aResults[i].fValue));
4809 : }
4810 :
4811 1 : m_pDoc->DeleteTab( 0 );
4812 1 : }
4813 :
4814 1 : void Test::testShiftCells()
4815 : {
4816 1 : m_pDoc->InsertTab(0, "foo");
4817 :
4818 1 : OUString aTestVal("Some Text");
4819 :
4820 : // Text into cell E5.
4821 1 : m_pDoc->SetString(4, 3, 0, aTestVal);
4822 :
4823 : // put a Note in cell E5
4824 1 : ScAddress rAddr(4, 3, 0);
4825 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
4826 1 : pNote->SetText(rAddr, "Hello");
4827 :
4828 1 : CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4829 :
4830 : // Insert cell at D5. This should shift the string cell to right.
4831 1 : m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
4832 2 : OUString aStr = m_pDoc->GetString(5, 3, 0);
4833 1 : CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
4834 1 : CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 3, 4, 3, 4));
4835 :
4836 1 : CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(4, 3, 0));
4837 1 : CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(5, 3, 0));
4838 :
4839 : // Delete cell D5, to shift the text cell back into D5.
4840 1 : m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
4841 1 : aStr = m_pDoc->GetString(4, 3, 0);
4842 1 : CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
4843 1 : CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 4, 4, 4, 4));
4844 :
4845 1 : CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(5, 3, 0));
4846 1 : CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4847 :
4848 2 : m_pDoc->DeleteTab(0);
4849 1 : }
4850 :
4851 1 : void Test::testNoteBasic()
4852 : {
4853 1 : m_pDoc->InsertTab(0, "PostIts");
4854 :
4855 1 : ScAddress aAddr(2, 2, 0); // cell C3
4856 1 : ScPostIt *pNote = m_pDoc->GetOrCreateNote(aAddr);
4857 :
4858 1 : pNote->SetText(aAddr, "Hello world");
4859 1 : pNote->SetAuthor("Jim Bob");
4860 :
4861 1 : ScPostIt *pGetNote = m_pDoc->GetNote(aAddr);
4862 1 : CPPUNIT_ASSERT_MESSAGE("note should be itself", pGetNote == pNote);
4863 :
4864 : // Insert one row at row 1.
4865 1 : bool bInsertRow = m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 1);
4866 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
4867 :
4868 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNote(aAddr) == NULL);
4869 1 : aAddr.IncRow(); // cell C4
4870 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNote(aAddr) == pNote);
4871 :
4872 : // Insert column at column A.
4873 1 : bool bInsertCol = m_pDoc->InsertCol(0, 0, MAXROW, 0, 1, 1);
4874 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
4875 :
4876 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNote(aAddr) == NULL);
4877 1 : aAddr.IncCol(); // cell D4
4878 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNote(aAddr) == pNote);
4879 :
4880 : // Insert a new sheet to shift the current sheet to the right.
4881 1 : m_pDoc->InsertTab(0, "Table2");
4882 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNote(aAddr) == NULL);
4883 1 : aAddr.IncTab(); // Move to the next sheet.
4884 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNote(aAddr) == pNote);
4885 :
4886 1 : m_pDoc->DeleteTab(0);
4887 1 : aAddr.IncTab(-1);
4888 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNote(aAddr) == pNote);
4889 :
4890 : // Insert cell at C4. This should NOT shift the note position.
4891 1 : bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
4892 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
4893 1 : CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNote(aAddr) == pNote);
4894 :
4895 : // Delete cell at C4. Again, this should NOT shift the note position.
4896 1 : m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
4897 1 : CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNote(aAddr) == pNote);
4898 :
4899 : // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
4900 1 : m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
4901 1 : aAddr.IncRow(-1); // cell D3
4902 1 : CPPUNIT_ASSERT_MESSAGE("Note at D4 should have shifted up to D3.", m_pDoc->GetNote(aAddr) == pNote);
4903 :
4904 : // Delete column C. This should shift the note one cell left.
4905 1 : m_pDoc->DeleteCol(0, 0, MAXROW, 0, 2, 1);
4906 1 : aAddr.IncCol(-1); // cell C3
4907 1 : CPPUNIT_ASSERT_MESSAGE("Note at D3 should have shifted left to C3.", m_pDoc->GetNote(aAddr) == pNote);
4908 :
4909 : // Insert a text where the note is.
4910 1 : m_pDoc->SetString(aAddr, "Note is here.");
4911 :
4912 : // Delete row 1. This should shift the note from C3 to C2.
4913 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 1);
4914 1 : aAddr.IncRow(-1); // C2
4915 1 : CPPUNIT_ASSERT_MESSAGE("Note at C3 should have shifted up to C2.", m_pDoc->GetNote(aAddr) == pNote);
4916 :
4917 1 : m_pDoc->DeleteTab(0);
4918 1 : }
4919 :
4920 1 : void Test::testNoteDeleteRow()
4921 : {
4922 1 : m_pDoc->InsertTab(0, "Sheet1");
4923 :
4924 : // We need a drawing layer in order to create caption objects.
4925 1 : m_pDoc->InitDrawLayer(&getDocShell());
4926 :
4927 1 : OUString aHello("Hello");
4928 2 : OUString aJimBob("Jim Bob");
4929 1 : ScAddress aPos(1, 1, 0);
4930 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
4931 1 : pNote->SetText(aPos, aHello);
4932 1 : pNote->SetAuthor(aJimBob);
4933 :
4934 1 : CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
4935 :
4936 : // test with IsBlockEmpty
4937 1 : bool bIgnoreNotes = true;
4938 1 : CPPUNIT_ASSERT_MESSAGE("The Block should be detected as empty (no Notes)", m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100, bIgnoreNotes));
4939 1 : bIgnoreNotes = false;
4940 1 : CPPUNIT_ASSERT_MESSAGE("The Block should NOT be detected as empty", !m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100, bIgnoreNotes));
4941 :
4942 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 1, 1);
4943 :
4944 1 : CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
4945 :
4946 : // Set values and notes into B3:B4.
4947 1 : aPos = ScAddress(1,2,0); // B3
4948 1 : m_pDoc->SetString(aPos, "First");
4949 1 : ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, "First Note", false, false);
4950 :
4951 1 : aPos = ScAddress(1,3,0); // B4
4952 1 : m_pDoc->SetString(aPos, "Second");
4953 1 : ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, "Second Note", false, false);
4954 :
4955 : // Delete row 2.
4956 1 : ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
4957 2 : ScMarkData aMark;
4958 1 : aMark.SelectOneTable(0);
4959 1 : rDocFunc.DeleteCells(ScRange(0,1,0,MAXCOL,1,0), &aMark, DEL_CELLSUP, true, true);
4960 :
4961 : // Check to make sure the notes have shifted upward.
4962 1 : pNote = m_pDoc->GetNote(ScAddress(1,1,0));
4963 1 : CPPUNIT_ASSERT_MESSAGE("B2 should have a note.", pNote);
4964 1 : CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
4965 1 : pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4966 1 : CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4967 1 : CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
4968 1 : pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4969 1 : CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
4970 :
4971 : // Undo.
4972 :
4973 1 : SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4974 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
4975 1 : m_pDoc->CreateAllNoteCaptions(); // to make sure that all notes have their corresponding caption objects...
4976 :
4977 1 : pUndoMgr->Undo();
4978 1 : pNote = m_pDoc->GetNote(ScAddress(1,1,0));
4979 1 : CPPUNIT_ASSERT_MESSAGE("B2 should NOT have a note.", !pNote);
4980 1 : pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4981 1 : CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4982 1 : CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
4983 1 : pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4984 1 : CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
4985 1 : CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
4986 :
4987 : // Delete row 3.
4988 1 : rDocFunc.DeleteCells(ScRange(0,2,0,MAXCOL,2,0), &aMark, DEL_CELLSUP, true, true);
4989 :
4990 1 : pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4991 1 : CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4992 1 : CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
4993 1 : pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4994 1 : CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
4995 :
4996 : // Undo and check the result.
4997 1 : pUndoMgr->Undo();
4998 1 : pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4999 1 : CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5000 1 : CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
5001 1 : pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5002 1 : CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
5003 1 : CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
5004 :
5005 2 : m_pDoc->DeleteTab(0);
5006 1 : }
5007 :
5008 1 : void Test::testNoteDeleteCol()
5009 : {
5010 1 : ScDocument* pDoc = getDocShell().GetDocument();
5011 1 : pDoc->InsertTab(0, "Sheet1");
5012 :
5013 1 : ScAddress rAddr(1, 1, 0);
5014 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5015 1 : pNote->SetText(rAddr, "Hello");
5016 1 : pNote->SetAuthor("Jim Bob");
5017 :
5018 1 : CPPUNIT_ASSERT_MESSAGE("there should be a note", pDoc->HasNote(1, 1, 0));
5019 :
5020 1 : pDoc->DeleteCol(0, 0, MAXROW, 0, 1, 1);
5021 :
5022 1 : CPPUNIT_ASSERT_MESSAGE("there should be no more note", !pDoc->HasNote(1, 1, 0));
5023 :
5024 1 : pDoc->DeleteTab(0);
5025 1 : }
5026 :
5027 1 : void Test::testNoteLifeCycle()
5028 : {
5029 1 : m_pDoc->InsertTab(0, "Test");
5030 :
5031 : // We need a drawing layer in order to create caption objects.
5032 1 : m_pDoc->InitDrawLayer(&getDocShell());
5033 :
5034 1 : ScAddress aPos(1,1,0);
5035 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5036 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new cell comment.", pNote);
5037 :
5038 1 : pNote->SetText(aPos, "New note");
5039 1 : ScPostIt* pNote2 = m_pDoc->ReleaseNote(aPos);
5040 1 : CPPUNIT_ASSERT_MESSAGE("This note instance is expected to be identical to the original.", pNote == pNote2);
5041 1 : CPPUNIT_ASSERT_MESSAGE("The note shouldn't be here after it's been released.", !m_pDoc->HasNote(aPos));
5042 :
5043 : // Modify the internal state of the note instance to make sure it's really
5044 : // been released.
5045 1 : pNote->SetText(aPos, "New content");
5046 :
5047 : // Re-insert the note back to the same place.
5048 1 : m_pDoc->SetNote(aPos, pNote);
5049 1 : const SdrCaptionObj* pCaption = pNote->GetOrCreateCaption(aPos);
5050 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create a caption object.", pCaption);
5051 2 : CPPUNIT_ASSERT_MESSAGE("This caption should belong to the drawing layer of the document.",
5052 1 : pCaption->GetModel() == m_pDoc->GetDrawLayer());
5053 :
5054 : // Copy B2 with note to a clipboard.
5055 :
5056 1 : ScClipParam aClipParam(aPos, false);
5057 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
5058 2 : ScMarkData aMarkData;
5059 1 : aMarkData.SelectOneTable(0);
5060 1 : m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMarkData, false, false, true, true, false);
5061 :
5062 1 : ScPostIt* pClipNote = aClipDoc.GetNote(aPos);
5063 1 : CPPUNIT_ASSERT_MESSAGE("Failed to copy note to the clipboard.", pClipNote);
5064 2 : CPPUNIT_ASSERT_MESSAGE("Note on the clipboard should share the same caption object from the original.",
5065 1 : pClipNote->GetCaption() == pCaption);
5066 :
5067 2 : m_pDoc->DeleteTab(0);
5068 1 : }
5069 :
5070 1 : void Test::testAreasWithNotes()
5071 : {
5072 1 : ScDocument* pDoc = getDocShell().GetDocument();
5073 1 : pDoc->InsertTab(0, "Sheet1");
5074 :
5075 1 : ScAddress rAddr(1, 5, 0);
5076 1 : ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5077 1 : pNote->SetText(rAddr, "Hello");
5078 1 : pNote->SetAuthor("Jim Bob");
5079 1 : ScAddress rAddrMin(2, 2, 0);
5080 1 : ScPostIt* pNoteMin = m_pDoc->GetOrCreateNote(rAddrMin);
5081 1 : pNoteMin->SetText(rAddrMin, "Hello");
5082 :
5083 : SCCOL col;
5084 : SCROW row;
5085 1 : bool dataFound = false;
5086 :
5087 : // only cell notes (empty content)
5088 :
5089 1 : dataFound = pDoc->GetDataStart(0,col,row);
5090 :
5091 1 : CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5092 1 : CPPUNIT_ASSERT_MESSAGE("DataStart wrong col for notes", col == 1);
5093 1 : CPPUNIT_ASSERT_MESSAGE("DataStart wrong row for notes", row == 2);
5094 :
5095 1 : dataFound = pDoc->GetCellArea(0,col,row);
5096 :
5097 1 : CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5098 1 : CPPUNIT_ASSERT_MESSAGE("CellArea wrong col for notes", col == 2);
5099 1 : CPPUNIT_ASSERT_MESSAGE("CellArea wrong row for notes", row == 5);
5100 :
5101 1 : bool bNotes = true;
5102 1 : dataFound = pDoc->GetPrintArea(0,col,row, bNotes);
5103 :
5104 1 : CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5105 1 : CPPUNIT_ASSERT_MESSAGE("PrintArea wrong col for notes", col == 2);
5106 1 : CPPUNIT_ASSERT_MESSAGE("PrintArea wrong row for notes", row == 5);
5107 :
5108 1 : bNotes = false;
5109 1 : dataFound = pDoc->GetPrintArea(0,col,row, bNotes);
5110 1 : CPPUNIT_ASSERT_MESSAGE("No PrintArea should be found", !dataFound);
5111 :
5112 1 : bNotes = true;
5113 1 : dataFound = pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5114 1 : CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5115 1 : CPPUNIT_ASSERT_MESSAGE("PrintAreaVer wrong row for notes", row == 5);
5116 :
5117 1 : dataFound = pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5118 1 : CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5119 1 : CPPUNIT_ASSERT_MESSAGE("PrintAreaVer wrong row for notes", row == 2);
5120 :
5121 1 : bNotes = false;
5122 1 : dataFound = pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // col 0 & 1
5123 1 : CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer should be found", !dataFound);
5124 :
5125 : // now add cells with value, check that notes are taken into accompt in good cases
5126 :
5127 1 : m_pDoc->SetString(0, 3, 0, "Some Text");
5128 1 : m_pDoc->SetString(3, 3, 0, "Some Text");
5129 :
5130 1 : dataFound = pDoc->GetDataStart(0,col,row);
5131 :
5132 1 : CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5133 1 : CPPUNIT_ASSERT_MESSAGE("DataStart wrong col", col == 0);
5134 1 : CPPUNIT_ASSERT_MESSAGE("DataStart wrong row", row == 2);
5135 :
5136 1 : dataFound = pDoc->GetCellArea(0,col,row);
5137 :
5138 1 : CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5139 1 : CPPUNIT_ASSERT_MESSAGE("CellArea wrong col", col == 3);
5140 1 : CPPUNIT_ASSERT_MESSAGE("CellArea wrong row", row == 5);
5141 :
5142 1 : bNotes = true;
5143 1 : dataFound = pDoc->GetPrintArea(0,col,row, bNotes);
5144 :
5145 1 : CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5146 1 : CPPUNIT_ASSERT_MESSAGE("PrintArea wrong col", col == 3);
5147 1 : CPPUNIT_ASSERT_MESSAGE("PrintArea wrong row", row == 5);
5148 :
5149 1 : bNotes = false;
5150 1 : dataFound = pDoc->GetPrintArea(0,col,row, bNotes);
5151 1 : CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5152 1 : CPPUNIT_ASSERT_MESSAGE("PrintArea wrong col", col == 3);
5153 1 : CPPUNIT_ASSERT_MESSAGE("PrintArea wrong row", row == 3);
5154 :
5155 1 : bNotes = true;
5156 1 : dataFound = pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5157 1 : CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5158 1 : CPPUNIT_ASSERT_MESSAGE("PrintAreaVer wrong row", row == 5);
5159 :
5160 1 : dataFound = pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5161 1 : CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5162 1 : CPPUNIT_ASSERT_MESSAGE("PrintAreaVer wrong row", row == 3);
5163 :
5164 1 : bNotes = false;
5165 1 : dataFound = pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5166 1 : CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5167 1 : CPPUNIT_ASSERT_MESSAGE("PrintAreaVer wrong row", row == 3);
5168 :
5169 1 : pDoc->DeleteTab(0);
5170 1 : }
5171 :
5172 1 : void Test::testAnchoredRotatedShape()
5173 : {
5174 1 : m_pDoc->InsertTab(0, "TestTab");
5175 : SCROW nRow1, nRow2;
5176 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5177 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
5178 :
5179 1 : m_pDoc->InitDrawLayer();
5180 1 : ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5181 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
5182 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
5183 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
5184 1 : m_pDoc->SetRowHeightRange( 0, MAXROW, 0, sc::HMMToTwips( 1000 ) );
5185 1 : const long TOLERANCE = 30; //30 hmm
5186 1024 : for ( SCCOL nCol = 0; nCol < MAXCOL; ++nCol )
5187 1023 : m_pDoc->SetColWidth( nCol, 0, sc::HMMToTwips( 1000 ) );
5188 : {
5189 : //Add a rect
5190 1 : Rectangle aRect( 4000, 5000, 10000, 7000 );
5191 :
5192 1 : Rectangle aRotRect( 6000, 3000, 8000, 9000 );
5193 1 : SdrRectObj *pObj = new SdrRectObj(aRect);
5194 1 : pPage->InsertObject(pObj);
5195 1 : Point aRef1(pObj->GetSnapRect().Center());
5196 1 : int nAngle = 9000; //90 deg.
5197 1 : double nSin=sin(nAngle*nPi180);
5198 1 : double nCos=cos(nAngle*nPi180);
5199 1 : pObj->Rotate(aRef1,nAngle,nSin,nCos);
5200 :
5201 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5202 :
5203 1 : Rectangle aSnap = pObj->GetSnapRect();
5204 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
5205 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
5206 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Left(), aSnap.Left(), TOLERANCE ) );
5207 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Top(), aSnap.Top(), TOLERANCE ) );
5208 :
5209 1 : ScDrawObjData aAnchor;
5210 1 : ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
5211 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5212 :
5213 1 : aAnchor.maStart = pData->maStart;
5214 1 : aAnchor.maEnd = pData->maEnd;
5215 :
5216 1 : m_pDoc->SetDrawPageSize(0);
5217 :
5218 : // increase row 5 by 2000 hmm
5219 1 : m_pDoc->SetRowHeight( 5, 0, sc::HMMToTwips( 3000 ) );
5220 : // increase col 6 by 1000 hmm
5221 1 : m_pDoc->SetColWidth( 6, 0, sc::HMMToTwips( 2000 ) );
5222 :
5223 1 : aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
5224 1 : aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
5225 :
5226 1 : m_pDoc->SetDrawPageSize(0);
5227 :
5228 1 : aSnap = pObj->GetSnapRect();
5229 :
5230 : // ensure that width and height have been adjusted accordingly
5231 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
5232 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
5233 :
5234 : // ensure that anchor start and end addresses haven't changed
5235 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
5236 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
5237 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
5238 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
5239 : }
5240 1 : m_pDoc->DeleteTab(0);
5241 1 : }
5242 :
5243 1 : void Test::testCellTextWidth()
5244 : {
5245 1 : m_pDoc->InsertTab(0, "Test");
5246 :
5247 1 : ScAddress aTopCell(0, 0, 0);
5248 :
5249 : // Sheet is empty.
5250 1 : boost::scoped_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
5251 1 : CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
5252 :
5253 : // Sheet only has one cell.
5254 1 : m_pDoc->SetString(0, 0, 0, "Only one cell");
5255 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
5256 1 : CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
5257 1 : SCROW nTestRow = 0;
5258 1 : CPPUNIT_ASSERT_EQUAL(nTestRow, pIter->getPos());
5259 :
5260 : // Setting a text width here should commit it to the column.
5261 1 : sal_uInt16 nTestVal = 432;
5262 1 : pIter->setValue(nTestVal);
5263 1 : CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
5264 :
5265 : // Set values to row 2 through 6.
5266 6 : for (SCROW i = 2; i <= 6; ++i)
5267 5 : m_pDoc->SetString(0, i, 0, "foo");
5268 :
5269 : // Set values to row 10 through 18.
5270 10 : for (SCROW i = 10; i <= 18; ++i)
5271 9 : m_pDoc->SetString(0, i, 0, "foo");
5272 :
5273 : {
5274 : // Full range.
5275 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
5276 1 : SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
5277 1 : size_t n = SAL_N_ELEMENTS(aRows);
5278 16 : for (size_t i = 0; i < n; ++i, pIter->next())
5279 : {
5280 15 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5281 15 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5282 : }
5283 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5284 : }
5285 :
5286 : {
5287 : // Specify start and end rows (6 - 16)
5288 1 : ScAddress aStart = aTopCell;
5289 1 : aStart.SetRow(6);
5290 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
5291 1 : SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
5292 1 : size_t n = SAL_N_ELEMENTS(aRows);
5293 9 : for (size_t i = 0; i < n; ++i, pIter->next())
5294 : {
5295 8 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5296 8 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5297 : }
5298 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5299 : }
5300 :
5301 : // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
5302 1 : clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
5303 :
5304 : {
5305 : // Full range again.
5306 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
5307 1 : SCROW aRows[] = { 0, 2, 18 };
5308 1 : size_t n = SAL_N_ELEMENTS(aRows);
5309 4 : for (size_t i = 0; i < n; ++i, pIter->next())
5310 : {
5311 3 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5312 3 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5313 : }
5314 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5315 : }
5316 :
5317 : // Delete row 2 which shifts all cells below row 2 upward. After this, we
5318 : // should only have cells at rows 0 and 17.
5319 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, MAXTAB, 2, 1);
5320 : {
5321 : // Full range again.
5322 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
5323 1 : SCROW aRows[] = { 0, 17 };
5324 1 : size_t n = SAL_N_ELEMENTS(aRows);
5325 3 : for (size_t i = 0; i < n; ++i, pIter->next())
5326 : {
5327 2 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5328 2 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5329 : }
5330 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5331 : }
5332 :
5333 1 : m_pDoc->DeleteTab(0);
5334 1 : }
5335 :
5336 5 : bool checkEditTextIterator(sc::EditTextIterator& rIter, const char** pChecks)
5337 : {
5338 5 : const EditTextObject* pText = rIter.first();
5339 5 : const char* p = *pChecks;
5340 :
5341 24 : for (int i = 0; i < 100; ++i) // cap it to 100 loops.
5342 : {
5343 24 : if (!pText)
5344 : // No more edit cells. The check string array should end too.
5345 5 : return p == NULL;
5346 :
5347 19 : if (!p)
5348 : // More edit cell, but no more check string. Bad.
5349 0 : return false;
5350 :
5351 19 : if (pText->GetParagraphCount() != 1)
5352 : // For this test, we don't handle multi-paragraph text.
5353 0 : return false;
5354 :
5355 19 : if (pText->GetText(0) != OUString::createFromAscii(p))
5356 : // Text differs from what's expected.
5357 0 : return false;
5358 :
5359 19 : pText = rIter.next();
5360 19 : ++pChecks;
5361 19 : p = *pChecks;
5362 : }
5363 :
5364 0 : return false;
5365 : }
5366 :
5367 1 : void Test::testEditTextIterator()
5368 : {
5369 1 : m_pDoc->InsertTab(0, "Test");
5370 :
5371 : {
5372 : // First, try with an empty sheet.
5373 1 : sc::EditTextIterator aIter(*m_pDoc,0);
5374 1 : const char* pChecks[] = { NULL };
5375 1 : CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5376 : }
5377 :
5378 1 : ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
5379 :
5380 : {
5381 : // Only set one edit cell.
5382 1 : rEditEngine.SetText("A2");
5383 1 : m_pDoc->SetEditText(ScAddress(0,1,0), rEditEngine.CreateTextObject());
5384 1 : sc::EditTextIterator aIter(*m_pDoc,0);
5385 1 : const char* pChecks[] = { "A2", NULL };
5386 1 : CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5387 : }
5388 :
5389 : {
5390 : // Add a series of edit cells.
5391 1 : rEditEngine.SetText("A5");
5392 1 : m_pDoc->SetEditText(ScAddress(0,4,0), rEditEngine.CreateTextObject());
5393 1 : rEditEngine.SetText("A6");
5394 1 : m_pDoc->SetEditText(ScAddress(0,5,0), rEditEngine.CreateTextObject());
5395 1 : rEditEngine.SetText("A7");
5396 1 : m_pDoc->SetEditText(ScAddress(0,6,0), rEditEngine.CreateTextObject());
5397 1 : sc::EditTextIterator aIter(*m_pDoc,0);
5398 1 : const char* pChecks[] = { "A2", "A5", "A6", "A7", NULL };
5399 1 : CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5400 : }
5401 :
5402 : {
5403 : // Add more edit cells to column C. Skip column B.
5404 1 : rEditEngine.SetText("C1");
5405 1 : m_pDoc->SetEditText(ScAddress(2,0,0), rEditEngine.CreateTextObject());
5406 1 : rEditEngine.SetText("C3");
5407 1 : m_pDoc->SetEditText(ScAddress(2,2,0), rEditEngine.CreateTextObject());
5408 1 : rEditEngine.SetText("C4");
5409 1 : m_pDoc->SetEditText(ScAddress(2,3,0), rEditEngine.CreateTextObject());
5410 1 : sc::EditTextIterator aIter(*m_pDoc,0);
5411 1 : const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", NULL };
5412 1 : CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5413 : }
5414 :
5415 : {
5416 : // Add some numeric, string and formula cells. This shouldn't affect the outcome.
5417 1 : m_pDoc->SetString(ScAddress(0,99,0), "=ROW()");
5418 1 : m_pDoc->SetValue(ScAddress(1,3,0), 1.2);
5419 1 : m_pDoc->SetString(ScAddress(2,4,0), "Simple string");
5420 1 : sc::EditTextIterator aIter(*m_pDoc,0);
5421 1 : const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", NULL };
5422 1 : CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5423 : }
5424 :
5425 1 : m_pDoc->DeleteTab(0);
5426 1 : }
5427 :
5428 1 : void Test::testCondFormatINSDEL()
5429 : {
5430 : // fdo#62206
5431 1 : m_pDoc->InsertTab(0, "Test");
5432 1 : ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
5433 :
5434 1 : ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
5435 1 : ScRangeList aRangeList(ScRange(0,0,0,0,3,0));
5436 1 : pFormat->AddRange(aRangeList);
5437 1 : ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
5438 1 : pFormat->AddEntry(pEntry);
5439 :
5440 1 : m_pDoc->AddCondFormatData(pFormat->GetRange(), 0, 1);
5441 1 : pList->InsertNew(pFormat);
5442 :
5443 1 : m_pDoc->InsertCol(0,0,MAXROW,0,0,2);
5444 1 : const ScRangeList& rRange = pFormat->GetRange();
5445 1 : CPPUNIT_ASSERT(rRange == ScRange(2,0,0,2,3,0));
5446 :
5447 2 : OUString aExpr = pEntry->GetExpression(ScAddress(2,0,0), 0);
5448 1 : CPPUNIT_ASSERT_EQUAL(aExpr, OUString("D2"));
5449 :
5450 2 : m_pDoc->DeleteTab(0);
5451 1 : }
5452 :
5453 1 : void Test::testCondFormatInsertCol()
5454 : {
5455 1 : m_pDoc->InsertTab(0, "Test");
5456 1 : ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
5457 :
5458 1 : ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
5459 1 : ScRangeList aRangeList(ScRange(0,0,0,3,3,0));
5460 1 : pFormat->AddRange(aRangeList);
5461 :
5462 1 : ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
5463 1 : pFormat->AddEntry(pEntry);
5464 :
5465 1 : m_pDoc->AddCondFormatData(pFormat->GetRange(), 0, 1);
5466 1 : pList->InsertNew(pFormat);
5467 :
5468 1 : m_pDoc->InsertCol(0,0,MAXROW,0,4,2);
5469 1 : const ScRangeList& rRange = pFormat->GetRange();
5470 1 : CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,0,0,5,3,0)), rRange);
5471 :
5472 1 : m_pDoc->DeleteTab(0);
5473 1 : }
5474 :
5475 1 : void Test::testCondFormatInsertRow()
5476 : {
5477 1 : m_pDoc->InsertTab(0, "Test");
5478 1 : ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
5479 :
5480 1 : ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
5481 1 : ScRangeList aRangeList(ScRange(0,0,0,3,3,0));
5482 1 : pFormat->AddRange(aRangeList);
5483 :
5484 1 : ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
5485 1 : pFormat->AddEntry(pEntry);
5486 :
5487 1 : m_pDoc->AddCondFormatData(pFormat->GetRange(), 0, 1);
5488 1 : pList->InsertNew(pFormat);
5489 :
5490 1 : m_pDoc->InsertRow(0,0,MAXCOL,0,4,2);
5491 1 : const ScRangeList& rRange = pFormat->GetRange();
5492 1 : CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,0,0,3,5,0)), rRange);
5493 :
5494 1 : m_pDoc->DeleteTab(0);
5495 1 : }
5496 :
5497 1 : void Test::testCondCopyPaste()
5498 : {
5499 1 : m_pDoc->InsertTab(0, "Test");
5500 :
5501 1 : ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
5502 1 : ScRange aCondFormatRange(0,0,0,3,3,0);
5503 1 : ScRangeList aRangeList(aCondFormatRange);
5504 1 : pFormat->AddRange(aRangeList);
5505 :
5506 1 : ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
5507 1 : pFormat->AddEntry(pEntry);
5508 1 : sal_uLong nIndex = m_pDoc->AddCondFormat(pFormat, 0);
5509 :
5510 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
5511 1 : copyToClip(m_pDoc, aCondFormatRange, &aClipDoc);
5512 :
5513 1 : ScRange aTargetRange(4,4,0,7,7,0);
5514 1 : pasteFromClip(m_pDoc, aTargetRange, &aClipDoc);
5515 :
5516 1 : ScConditionalFormat* pPastedFormat = m_pDoc->GetCondFormat(7,7,0);
5517 1 : CPPUNIT_ASSERT(pPastedFormat);
5518 :
5519 1 : CPPUNIT_ASSERT_EQUAL(ScRangeList(aTargetRange), pPastedFormat->GetRange());
5520 1 : CPPUNIT_ASSERT( nIndex != pPastedFormat->GetKey());
5521 1 : const SfxPoolItem* pItem = m_pDoc->GetAttr( 7, 7, 0, ATTR_CONDITIONAL );
5522 1 : const ScCondFormatItem* pCondFormatItem = static_cast<const ScCondFormatItem*>(pItem);
5523 :
5524 1 : CPPUNIT_ASSERT(pCondFormatItem);
5525 1 : CPPUNIT_ASSERT_EQUAL(size_t(1), pCondFormatItem->GetCondFormatData().size());
5526 1 : CPPUNIT_ASSERT( nIndex != pCondFormatItem->GetCondFormatData().at(0) );
5527 :
5528 :
5529 2 : m_pDoc->DeleteTab(0);
5530 1 : }
5531 :
5532 1 : void Test::testImportStream()
5533 : {
5534 1 : sc::AutoCalcSwitch aAC(*m_pDoc, true); // turn on auto calc.
5535 2 : sc::UndoSwitch aUndo(*m_pDoc, true); // enable undo.
5536 :
5537 1 : m_pDoc->InsertTab(0, "Test");
5538 :
5539 1 : m_pDoc->SetString(ScAddress(0,1,0), "=SUM(A1:C1)"); // A2
5540 :
5541 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5542 :
5543 : // CSV import options.
5544 2 : ScAsciiOptions aOpt;
5545 1 : aOpt.SetFieldSeps(",");
5546 :
5547 : // Import values to A1:C1.
5548 2 : ScImportExport aObj(m_pDoc, ScAddress(0,0,0));
5549 1 : aObj.SetImportBroadcast(true);
5550 1 : aObj.SetExtOptions(aOpt);
5551 1 : aObj.ImportString("1,2,3", FORMAT_STRING);
5552 :
5553 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5554 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5555 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5556 :
5557 : // Formula value should have been updated.
5558 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5559 :
5560 : // Undo, and check the result.
5561 1 : SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5562 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get the undo manager.", pUndoMgr);
5563 1 : pUndoMgr->Undo();
5564 :
5565 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5566 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5567 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5568 :
5569 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5570 :
5571 : // Redo, and check the result.
5572 1 : pUndoMgr->Redo();
5573 :
5574 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5575 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5576 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5577 :
5578 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5579 :
5580 1 : pUndoMgr->Clear();
5581 :
5582 2 : m_pDoc->DeleteTab(0);
5583 1 : }
5584 :
5585 1 : void Test::testDeleteContents()
5586 : {
5587 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
5588 2 : sc::UndoSwitch aUndoSwitch(*m_pDoc, true); // enable undo.
5589 :
5590 1 : m_pDoc->InsertTab(0, "Test");
5591 :
5592 1 : m_pDoc->SetValue(ScAddress(3,1,0), 1.0);
5593 1 : m_pDoc->SetValue(ScAddress(3,2,0), 1.0);
5594 1 : m_pDoc->SetValue(ScAddress(3,3,0), 1.0);
5595 1 : m_pDoc->SetValue(ScAddress(3,4,0), 1.0);
5596 1 : m_pDoc->SetValue(ScAddress(3,5,0), 1.0);
5597 1 : m_pDoc->SetValue(ScAddress(3,6,0), 1.0);
5598 1 : m_pDoc->SetValue(ScAddress(3,7,0), 1.0);
5599 1 : m_pDoc->SetValue(ScAddress(3,8,0), 1.0);
5600 1 : m_pDoc->SetString(ScAddress(3,15,0), "=SUM(D2:D15)");
5601 :
5602 1 : CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5603 :
5604 : // Delete D2:D6.
5605 1 : ScRange aRange(3,1,0,3,5,0);
5606 2 : ScMarkData aMark;
5607 1 : aMark.SelectOneTable(0);
5608 1 : aMark.SetMarkArea(aRange);
5609 :
5610 1 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
5611 1 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
5612 1 : m_pDoc->CopyToDocument(aRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
5613 2 : ScUndoDeleteContents aUndo(&getDocShell(), aMark, aRange, pUndoDoc, false, IDF_CONTENTS, true);
5614 :
5615 1 : clearRange(m_pDoc, aRange);
5616 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5617 :
5618 1 : aUndo.Undo();
5619 1 : CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5620 :
5621 1 : aUndo.Redo();
5622 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5623 :
5624 2 : m_pDoc->DeleteTab(0);
5625 1 : }
5626 :
5627 1 : void Test::testTransliterateText()
5628 : {
5629 1 : m_pDoc->InsertTab(0, "Test");
5630 :
5631 : // Set texts to A1:A3.
5632 1 : m_pDoc->SetString(ScAddress(0,0,0), "Mike");
5633 1 : m_pDoc->SetString(ScAddress(0,1,0), "Noah");
5634 1 : m_pDoc->SetString(ScAddress(0,2,0), "Oscar");
5635 :
5636 : // Change them to uppercase.
5637 1 : ScMarkData aMark;
5638 1 : aMark.SetMarkArea(ScRange(0,0,0,0,2,0));
5639 1 : ScDocFunc& rFunc = getDocShell().GetDocFunc();
5640 : rFunc.TransliterateText(
5641 1 : aMark, i18n::TransliterationModules_LOWERCASE_UPPERCASE, true, true);
5642 :
5643 1 : CPPUNIT_ASSERT_EQUAL(OUString("MIKE"), m_pDoc->GetString(ScAddress(0,0,0)));
5644 1 : CPPUNIT_ASSERT_EQUAL(OUString("NOAH"), m_pDoc->GetString(ScAddress(0,1,0)));
5645 1 : CPPUNIT_ASSERT_EQUAL(OUString("OSCAR"), m_pDoc->GetString(ScAddress(0,2,0)));
5646 :
5647 : // Test the undo and redo.
5648 1 : SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5649 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5650 :
5651 1 : pUndoMgr->Undo();
5652 1 : CPPUNIT_ASSERT_EQUAL(OUString("Mike"), m_pDoc->GetString(ScAddress(0,0,0)));
5653 1 : CPPUNIT_ASSERT_EQUAL(OUString("Noah"), m_pDoc->GetString(ScAddress(0,1,0)));
5654 1 : CPPUNIT_ASSERT_EQUAL(OUString("Oscar"), m_pDoc->GetString(ScAddress(0,2,0)));
5655 :
5656 1 : pUndoMgr->Redo();
5657 1 : CPPUNIT_ASSERT_EQUAL(OUString("MIKE"), m_pDoc->GetString(ScAddress(0,0,0)));
5658 1 : CPPUNIT_ASSERT_EQUAL(OUString("NOAH"), m_pDoc->GetString(ScAddress(0,1,0)));
5659 1 : CPPUNIT_ASSERT_EQUAL(OUString("OSCAR"), m_pDoc->GetString(ScAddress(0,2,0)));
5660 :
5661 1 : m_pDoc->DeleteTab(0);
5662 1 : }
5663 :
5664 1 : void Test::testMixData()
5665 : {
5666 1 : m_pDoc->InsertTab(0, "Test");
5667 :
5668 1 : m_pDoc->SetValue(1,0,0,2);
5669 1 : m_pDoc->SetValue(0,1,0,3);
5670 1 : ScDocument aClipDoc(SCDOCMODE_CLIP);
5671 1 : copyToClip(m_pDoc, ScRange(0,0,0,1,0,0), &aClipDoc);
5672 :
5673 2 : ScDocument aMixDoc(SCDOCMODE_CLIP);
5674 1 : copyToClip(m_pDoc, ScRange(0,1,0,1,1,0), &aMixDoc);
5675 :
5676 1 : pasteFromClip(m_pDoc, ScRange(0,1,0,1,1,0), &aClipDoc);
5677 1 : m_pDoc->MixDocument(ScRange(0,1,0,1,1,0), 1, false, &aMixDoc);
5678 :
5679 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,1,0));
5680 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,1,0));
5681 :
5682 2 : m_pDoc->DeleteTab(0);
5683 1 : }
5684 :
5685 5 : ScDocShell* Test::findLoadedDocShellByName(const OUString& rName)
5686 : {
5687 5 : TypeId aType(TYPE(ScDocShell));
5688 5 : ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
5689 306 : while (pShell)
5690 : {
5691 299 : SfxMedium* pMedium = pShell->GetMedium();
5692 299 : if (pMedium)
5693 : {
5694 299 : OUString aName = pMedium->GetName();
5695 299 : if (aName.equals(rName))
5696 3 : return pShell;
5697 : }
5698 296 : pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
5699 : }
5700 2 : return NULL;
5701 : }
5702 :
5703 4 : bool Test::insertRangeNames(
5704 : ScDocument* pDoc, ScRangeName* pNames, const RangeNameDef* p, const RangeNameDef* pEnd)
5705 : {
5706 4 : ScAddress aA1(0, 0, 0);
5707 13 : for (; p != pEnd; ++p)
5708 : {
5709 : ScRangeData* pNew = new ScRangeData(
5710 : pDoc,
5711 : OUString::createFromAscii(p->mpName),
5712 : OUString::createFromAscii(p->mpExpr),
5713 9 : aA1, 0, formula::FormulaGrammar::GRAM_ENGLISH);
5714 9 : pNew->SetIndex(p->mnIndex);
5715 9 : bool bSuccess = pNames->insert(pNew);
5716 9 : if (!bSuccess)
5717 : {
5718 0 : cerr << "Insertion failed." << endl;
5719 0 : return false;
5720 : }
5721 : }
5722 :
5723 4 : return true;
5724 : }
5725 :
5726 39 : void Test::printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption)
5727 : {
5728 39 : SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
5729 39 : SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
5730 39 : SheetPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1);
5731 349 : for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5732 : {
5733 1185 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5734 : {
5735 875 : OUString aVal = pDoc->GetString(nCol, nRow, rRange.aStart.Tab());
5736 875 : printer.set(nRow-nRow1, nCol-nCol1, aVal);
5737 875 : }
5738 : }
5739 39 : printer.print(pCaption);
5740 39 : }
5741 :
5742 83 : void Test::clearRange(ScDocument* pDoc, const ScRange& rRange)
5743 : {
5744 83 : ScMarkData aMarkData;
5745 83 : aMarkData.SetMarkArea(rRange);
5746 : pDoc->DeleteArea(
5747 83 : rRange.aStart.Col(), rRange.aStart.Row(),
5748 166 : rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, IDF_CONTENTS);
5749 83 : }
5750 :
5751 5 : void Test::clearSheet(ScDocument* pDoc, SCTAB nTab)
5752 : {
5753 5 : ScRange aRange(0,0,nTab,MAXCOL,MAXROW,nTab);
5754 5 : clearRange(pDoc, aRange);
5755 5 : }
5756 :
5757 7 : void Test::copyToClip(ScDocument* pSrcDoc, const ScRange& rRange, ScDocument* pClipDoc)
5758 : {
5759 7 : ScClipParam aClipParam(rRange, false);
5760 14 : ScMarkData aMark;
5761 7 : aMark.SetMarkArea(rRange);
5762 14 : pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
5763 7 : }
5764 :
5765 3 : void Test::pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc)
5766 : {
5767 3 : ScMarkData aMark;
5768 3 : aMark.SetMarkArea(rDestRange);
5769 3 : pDestDoc->CopyFromClip(rDestRange, aMark, IDF_ALL, NULL, pClipDoc);
5770 3 : }
5771 :
5772 2 : ScUndoPaste* Test::createUndoPaste(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pUndoDoc)
5773 : {
5774 2 : ScDocument* pDoc = rDocSh.GetDocument();
5775 2 : ScMarkData aMarkData;
5776 2 : aMarkData.SetMarkArea(rRange);
5777 2 : ScRefUndoData* pRefUndoData = new ScRefUndoData(pDoc);
5778 :
5779 : return new ScUndoPaste(
5780 2 : &rDocSh, rRange, aMarkData, pUndoDoc, NULL, IDF_ALL, pRefUndoData, false);
5781 : }
5782 :
5783 1 : CPPUNIT_TEST_SUITE_REGISTRATION(Test);
5784 :
5785 4 : CPPUNIT_PLUGIN_IMPLEMENT();
5786 :
5787 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|