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 <sfx2/dispatch.hxx>
11 #include <svx/svdograf.hxx>
12 #include <svx/svdpage.hxx>
13 #include <test/calc_unoapi_test.hxx>
14 
15 #include <com/sun/star/sheet/XSpreadsheet.hpp>
16 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
17 #include <com/sun/star/text/XText.hpp>
18 
19 #include <docsh.hxx>
20 #include <drwlayer.hxx>
21 #include <scitems.hxx>
22 #include <svx/svdocirc.hxx>
23 #include <vcl/scheduler.hxx>
24 #include <tabvwsh.hxx>
25 
26 #include <sc.hrc>
27 
28 using namespace css;
29 
30 namespace sc_apitest
31 {
32 class ScAnchorTest : public CalcUnoApiTest
33 {
34 public:
35     ScAnchorTest();
36 
37     virtual void tearDown() override;
38 
39     void testUndoAnchor();
40     void testTdf76183();
41     void testODFAnchorTypes();
42     void testCopyColumnWithImages();
43     void testCutWithImages();
44     void testTdf129552();
45 
46     CPPUNIT_TEST_SUITE(ScAnchorTest);
47     CPPUNIT_TEST(testUndoAnchor);
48     CPPUNIT_TEST(testTdf76183);
49     CPPUNIT_TEST(testODFAnchorTypes);
50     CPPUNIT_TEST(testCopyColumnWithImages);
51     CPPUNIT_TEST(testCutWithImages);
52     CPPUNIT_TEST(testTdf129552);
53     CPPUNIT_TEST_SUITE_END();
54 
55 private:
56     uno::Reference<lang::XComponent> mxComponent;
57 };
58 
ScAnchorTest()59 ScAnchorTest::ScAnchorTest()
60     : CalcUnoApiTest("sc/qa/unit/data/ods")
61 {
62 }
63 
testUndoAnchor()64 void ScAnchorTest::testUndoAnchor()
65 {
66     OUString aFileURL;
67     createFileURL("document_with_linked_graphic.ods", aFileURL);
68     // open the document with graphic included
69     uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
70     CPPUNIT_ASSERT(xComponent.is());
71 
72     // Get the document model
73     SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
74     CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
75 
76     ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
77     CPPUNIT_ASSERT(pDocSh);
78 
79     // Check whether graphic imported well
80     ScDocument& rDoc = pDocSh->GetDocument();
81     ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
82     CPPUNIT_ASSERT(pDrawLayer);
83 
84     const SdrPage* pPage = pDrawLayer->GetPage(0);
85     CPPUNIT_ASSERT(pPage);
86 
87     SdrGrafObj* pObject = dynamic_cast<SdrGrafObj*>(pPage->GetObj(0));
88     CPPUNIT_ASSERT(pObject);
89     CPPUNIT_ASSERT(pObject->IsLinkedGraphic());
90 
91     const GraphicObject& rGraphicObj = pObject->GetGraphicObject(true);
92     CPPUNIT_ASSERT_EQUAL(int(GraphicType::Bitmap), int(rGraphicObj.GetGraphic().GetType()));
93     CPPUNIT_ASSERT_EQUAL(sal_uLong(864900), rGraphicObj.GetGraphic().GetSizeBytes());
94 
95     // Get the document controller
96     ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
97     CPPUNIT_ASSERT(pViewShell);
98 
99     // Get the draw view of the document
100     ScDrawView* pDrawView = pViewShell->GetViewData().GetScDrawView();
101     CPPUNIT_ASSERT(pDrawView);
102 
103     // Select graphic object
104     pDrawView->MarkNextObj();
105     CPPUNIT_ASSERT(pDrawView->AreObjectsMarked());
106 
107     // Set Cell Anchor
108     ScDrawLayer::SetCellAnchoredFromPosition(*pObject, rDoc, 0, false);
109     // Check state
110     ScAnchorType oldType = ScDrawLayer::GetAnchorType(*pObject);
111     CPPUNIT_ASSERT_EQUAL(SCA_CELL, oldType);
112 
113     // Change all selected objects to page anchor
114     pViewShell->GetViewData().GetDispatcher().Execute(SID_ANCHOR_PAGE);
115     // Check state
116     ScAnchorType newType = ScDrawLayer::GetAnchorType(*pObject);
117     CPPUNIT_ASSERT_EQUAL(SCA_PAGE, newType);
118 
119     // Undo and check its result.
120     SfxUndoManager* pUndoMgr = rDoc.GetUndoManager();
121     CPPUNIT_ASSERT(pUndoMgr);
122     pUndoMgr->Undo();
123 
124     // Check anchor type
125     CPPUNIT_ASSERT_EQUAL(oldType, ScDrawLayer::GetAnchorType(*pObject));
126     CPPUNIT_ASSERT_EQUAL(int(GraphicType::Bitmap), int(rGraphicObj.GetGraphic().GetType()));
127     CPPUNIT_ASSERT_EQUAL(sal_uLong(864900), rGraphicObj.GetGraphic().GetSizeBytes());
128 
129     pUndoMgr->Redo();
130 
131     // Check anchor type
132     CPPUNIT_ASSERT_EQUAL(newType, ScDrawLayer::GetAnchorType(*pObject));
133     CPPUNIT_ASSERT_EQUAL(int(GraphicType::Bitmap), int(rGraphicObj.GetGraphic().GetType()));
134     CPPUNIT_ASSERT_EQUAL(sal_uLong(864900), rGraphicObj.GetGraphic().GetSizeBytes());
135 
136     ScDrawLayer::SetPageAnchored(*pObject);
137     // Check state
138     oldType = ScDrawLayer::GetAnchorType(*pObject);
139     CPPUNIT_ASSERT_EQUAL(SCA_PAGE, oldType);
140 
141     // Change all selected objects to cell anchor
142     pViewShell->GetViewData().GetDispatcher().Execute(SID_ANCHOR_CELL);
143     // Check state
144     newType = ScDrawLayer::GetAnchorType(*pObject);
145     CPPUNIT_ASSERT_EQUAL(SCA_CELL, newType);
146 
147     pUndoMgr->Undo();
148 
149     // Check anchor type
150     CPPUNIT_ASSERT_EQUAL(oldType, ScDrawLayer::GetAnchorType(*pObject));
151     CPPUNIT_ASSERT_EQUAL(int(GraphicType::Bitmap), int(rGraphicObj.GetGraphic().GetType()));
152     CPPUNIT_ASSERT_EQUAL(sal_uLong(864900), rGraphicObj.GetGraphic().GetSizeBytes());
153 
154     pUndoMgr->Redo();
155 
156     // Check anchor type
157     CPPUNIT_ASSERT_EQUAL(newType, ScDrawLayer::GetAnchorType(*pObject));
158     CPPUNIT_ASSERT_EQUAL(int(GraphicType::Bitmap), int(rGraphicObj.GetGraphic().GetType()));
159     CPPUNIT_ASSERT_EQUAL(sal_uLong(864900), rGraphicObj.GetGraphic().GetSizeBytes());
160 
161     xComponent->dispose();
162 }
163 
testTdf76183()164 void ScAnchorTest::testTdf76183()
165 {
166     uno::Reference<lang::XComponent> xComponent = loadFromDesktop("private:factory/scalc");
167     SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
168     ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
169     ScDocument& rDoc = pDocSh->GetDocument();
170     ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
171     SdrPage* pPage = pDrawLayer->GetPage(0);
172 
173     // Add a circle somewhere below first row.
174     const tools::Rectangle aOrigRect(1000, 1000, 1200, 1200);
175     SdrCircObj* pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
176     pPage->InsertObject(pObj);
177     // Anchor to cell
178     ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, 0, false);
179     const tools::Rectangle& rNewRect = pObj->GetLogicRect();
180 
181     // Set word wrap to true
182     rDoc.ApplyAttr(0, 0, 0, SfxBoolItem(ATTR_LINEBREAK, true));
183     // Add multi-line text to cell to initiate optimal height change
184     uno::Reference<sheet::XSpreadsheetDocument> xDoc(xComponent, uno::UNO_QUERY_THROW);
185     uno::Reference<container::XIndexAccess> xIA(xDoc->getSheets(), uno::UNO_QUERY_THROW);
186     uno::Reference<sheet::XSpreadsheet> xSheet(xIA->getByIndex(0), uno::UNO_QUERY_THROW);
187     uno::Reference<text::XText> xText(xSheet->getCellByPosition(0, 0), uno::UNO_QUERY_THROW);
188     xText->setString("first\nsecond\nthird");
189 
190     // The resize of first row must have moved the object down after its anchor cell
191     CPPUNIT_ASSERT(aOrigRect.Top() < rNewRect.Top());
192 
193     pDocSh->DoClose();
194 }
195 
testODFAnchorTypes()196 void ScAnchorTest::testODFAnchorTypes()
197 {
198     OUString aFileURL;
199     createFileURL("3AnchorTypes.ods", aFileURL);
200     // open the document with graphic included
201     uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
202     CPPUNIT_ASSERT(xComponent.is());
203 
204     // Get the document model
205     SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
206     CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
207 
208     ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
209     CPPUNIT_ASSERT(pDocSh);
210 
211     // Check whether graphic imported well
212     ScDocument& rDoc = pDocSh->GetDocument();
213     ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
214     CPPUNIT_ASSERT(pDrawLayer);
215 
216     const SdrPage* pPage = pDrawLayer->GetPage(0);
217     CPPUNIT_ASSERT(pPage);
218 
219     // Check 1st object: Page anchored
220     SdrGrafObj* pObject = dynamic_cast<SdrGrafObj*>(pPage->GetObj(0));
221     CPPUNIT_ASSERT(pObject);
222     ScAnchorType anchorType = ScDrawLayer::GetAnchorType(*pObject);
223     CPPUNIT_ASSERT_EQUAL(SCA_PAGE, anchorType);
224 
225     // Check 2nd object: Cell anchored, resize with cell
226     pObject = dynamic_cast<SdrGrafObj*>(pPage->GetObj(1));
227     CPPUNIT_ASSERT(pObject);
228     anchorType = ScDrawLayer::GetAnchorType(*pObject);
229     CPPUNIT_ASSERT_EQUAL(SCA_CELL_RESIZE, anchorType);
230 
231     // Check 3rd object: Cell anchored
232     pObject = dynamic_cast<SdrGrafObj*>(pPage->GetObj(2));
233     CPPUNIT_ASSERT(pObject);
234     anchorType = ScDrawLayer::GetAnchorType(*pObject);
235     CPPUNIT_ASSERT_EQUAL(SCA_CELL, anchorType);
236 
237     pDocSh->DoClose();
238 }
239 
240 /// Test that copying a column with an image anchored to it also copies the image
testCopyColumnWithImages()241 void ScAnchorTest::testCopyColumnWithImages()
242 {
243     OUString aFileURL;
244     createFileURL("3AnchorTypes.ods", aFileURL);
245     // open the document with graphic included
246     uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
247     CPPUNIT_ASSERT(xComponent.is());
248 
249     // Get the document model
250     SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
251     CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
252 
253     ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
254     CPPUNIT_ASSERT(pDocSh);
255 
256     ScDocument* pDoc = &(pDocSh->GetDocument());
257     ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
258     CPPUNIT_ASSERT(pDrawLayer);
259 
260     // Get the document controller
261     ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
262     CPPUNIT_ASSERT(pViewShell != nullptr);
263 
264     ScDocument aClipDoc(SCDOCMODE_CLIP);
265 
266     // Copy whole column
267     {
268         // 1. Copy source range
269         ScRange aSrcRange;
270         aSrcRange.Parse("A1:A11", pDoc, pDoc->GetAddressConvention());
271         pViewShell->GetViewData().GetMarkData().SetMarkArea(aSrcRange);
272         pViewShell->GetViewData().GetView()->CopyToClip(&aClipDoc, false, false, true, false);
273 
274         // 2. Paste to target range
275         ScRange aDstRange;
276         aDstRange.Parse("D1:D11", pDoc, pDoc->GetAddressConvention());
277         pViewShell->GetViewData().GetMarkData().SetMarkArea(aDstRange);
278         pViewShell->GetViewData().GetView()->PasteFromClip(InsertDeleteFlags::ALL, &aClipDoc);
279 
280         // 3. Make sure the images have been copied too
281         std::map<SCROW, std::vector<SdrObject*>> aRowObjects
282             = pDrawLayer->GetObjectsAnchoredToRange(0, 3, 0, 11);
283         CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an image anchored to D3", 1,
284                                      static_cast<int>(aRowObjects[2].size()));
285         CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an image anchored to D11", 1,
286                                      static_cast<int>(aRowObjects[10].size()));
287     }
288 
289     // Copy individual cells
290     {
291         // 1. Copy source cells
292         ScRange aSrcRange;
293         aSrcRange.Parse("A3:B3", pDoc, pDoc->GetAddressConvention());
294         pViewShell->GetViewData().GetMarkData().SetMarkArea(aSrcRange);
295         pViewShell->GetViewData().GetView()->CopyToClip(&aClipDoc, false, false, true, false);
296 
297         // 2. Paste to target cells
298         ScRange aDstRange;
299         aDstRange.Parse("G3:H3", pDoc, pDoc->GetAddressConvention());
300         pViewShell->GetViewData().GetMarkData().SetMarkArea(aDstRange);
301         pViewShell->GetViewData().GetView()->PasteFromClip(InsertDeleteFlags::ALL, &aClipDoc);
302 
303         // 3. Make sure the image has been copied too
304         std::map<SCROW, std::vector<SdrObject*>> aRowObjects
305             = pDrawLayer->GetObjectsAnchoredToRange(0, 6, 2, 2);
306         CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an image anchored to G3", 1,
307                                      static_cast<int>(aRowObjects[2].size()));
308     }
309 
310     pDocSh->DoClose();
311 }
312 
testCutWithImages()313 void ScAnchorTest::testCutWithImages()
314 {
315     OUString aFileURL;
316     createFileURL("3AnchorTypes.ods", aFileURL);
317     // open the document with graphic included
318     uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
319     CPPUNIT_ASSERT(xComponent.is());
320 
321     // Get the document model
322     SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
323     CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
324 
325     ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
326     CPPUNIT_ASSERT(pDocSh);
327 
328     ScDocument* pDoc = &(pDocSh->GetDocument());
329     ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
330     CPPUNIT_ASSERT(pDrawLayer);
331 
332     // Get the document controller
333     ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
334     CPPUNIT_ASSERT(pViewShell != nullptr);
335 
336     // Cut whole column
337     {
338         // Cut source range
339         ScRange aSrcRange;
340         aSrcRange.Parse("A1:A11", pDoc, pDoc->GetAddressConvention());
341         pViewShell->GetViewData().GetMarkData().SetMarkArea(aSrcRange);
342         pViewShell->GetViewData().GetView()->CutToClip();
343 
344         std::map<SCROW, std::vector<SdrObject*>> aRowObjects
345             = pDrawLayer->GetObjectsAnchoredToRange(0, 0, 0, 11);
346 
347         // Images should have been removed from the cells
348         CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no image anchored to A3", 0,
349                                      static_cast<int>(aRowObjects[2].size()));
350         CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no image anchored to A11", 0,
351                                      static_cast<int>(aRowObjects[10].size()));
352     }
353 
354     // Cut individual cells
355     {
356         // Cut source cells
357         ScRange aSrcRange;
358         aSrcRange.Parse("A3:B3", pDoc, pDoc->GetAddressConvention());
359         pViewShell->GetViewData().GetMarkData().SetMarkArea(aSrcRange);
360         pViewShell->GetViewData().GetView()->CutToClip();
361 
362         // Image should have been removed from the cell
363         std::map<SCROW, std::vector<SdrObject*>> aRowObjects
364             = pDrawLayer->GetObjectsAnchoredToRange(0, 0, 2, 2);
365         CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no image anchored to A3", 0,
366                                      static_cast<int>(aRowObjects[2].size()));
367     }
368 
369     pDocSh->DoClose();
370 }
371 
testTdf129552()372 void ScAnchorTest::testTdf129552()
373 {
374     OUString aFileURL;
375     createFileURL("tdf129552.fods", aFileURL);
376     uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
377     CPPUNIT_ASSERT(xComponent.is());
378 
379     // Without the accompanying fix in place, this test would have never returned due to an infinite
380     // invalidation loop, where ScGridWindow::Paint() invalidated itself.
381     Scheduler::ProcessEventsToIdle();
382 
383     xComponent->dispose();
384 }
385 
tearDown()386 void ScAnchorTest::tearDown()
387 {
388     if (mxComponent.is())
389     {
390         closeDocument(mxComponent);
391     }
392 
393     CalcUnoApiTest::tearDown();
394 }
395 
396 CPPUNIT_TEST_SUITE_REGISTRATION(ScAnchorTest);
397 }
398 
399 CPPUNIT_PLUGIN_IMPLEMENT();
400 
401 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
402