Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sd / qa / unit / tiledrendering / tiledrendering.cxx
blob91a929a11d078fbf6e7b961bd5e32488ce2c0545
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <test/unoapixml_test.hxx>
12 #include <app.hrc>
13 #include <test/helper/transferable.hxx>
14 #include <boost/property_tree/json_parser.hpp>
15 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
16 #include <sal/log.hxx>
17 #include <sfx2/lokhelper.hxx>
18 #include <comphelper/propertysequence.hxx>
19 #include <comphelper/propertyvalue.hxx>
20 #include <comphelper/string.hxx>
21 #include <editeng/eeitem.hxx>
22 #include <editeng/editids.hrc>
23 #include <editeng/editobj.hxx>
24 #include <editeng/editview.hxx>
25 #include <editeng/numitem.hxx>
26 #include <editeng/outliner.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/outlobj.hxx>
29 #include <osl/conditn.hxx>
30 #include <sfx2/dispatch.hxx>
31 #include <sfx2/viewfrm.hxx>
32 #include <svl/stritem.hxx>
33 #include <svl/intitem.hxx>
34 #include <comphelper/lok.hxx>
35 #include <svx/svdotable.hxx>
36 #include <svx/svdoutl.hxx>
37 #include <unotools/datetime.hxx>
38 #include <test/lokcallback.hxx>
40 #include <DrawDocShell.hxx>
41 #include <ViewShellBase.hxx>
42 #include <ViewShell.hxx>
43 #include <sdpage.hxx>
44 #include <unomodel.hxx>
45 #include <drawdoc.hxx>
46 #include <undo/undomanager.hxx>
47 #include <sfx2/request.hxx>
48 #include <svx/svxids.hrc>
49 #include <pres.hxx>
50 #include <navigatr.hxx>
51 #include <vcl/cursor.hxx>
52 #include <vcl/scheduler.hxx>
53 #include <vcl/vclevent.hxx>
54 #include <vcl/BitmapReadAccess.hxx>
55 #include <vcl/virdev.hxx>
56 #include <o3tl/string_view.hxx>
58 #include <chrono>
59 #include <cstdlib>
60 #include <string_view>
62 using namespace css;
64 static std::ostream& operator<<(std::ostream& os, ViewShellId id)
66 os << static_cast<sal_Int32>(id);
67 return os;
70 class SdTiledRenderingTest : public UnoApiXmlTest
72 public:
73 SdTiledRenderingTest();
74 virtual void setUp() override;
75 virtual void tearDown() override;
77 protected:
78 SdXImpressDocument* createDoc(const char* pName, const uno::Sequence<beans::PropertyValue>& rArguments = uno::Sequence<beans::PropertyValue>());
79 void setupLibreOfficeKitViewCallback(SfxViewShell& pViewShell);
80 static void callback(int nType, const char* pPayload, void* pData);
81 void callbackImpl(int nType, const char* pPayload);
82 xmlDocUniquePtr parseXmlDump();
84 ::tools::Rectangle m_aInvalidation;
85 std::vector<::tools::Rectangle> m_aSelection;
86 bool m_bFound;
87 sal_Int32 m_nPart;
88 std::vector<OString> m_aSearchResultSelection;
89 std::vector<int> m_aSearchResultPart;
90 int m_nSelectionBeforeSearchResult;
91 int m_nSelectionAfterSearchResult;
93 /// For document size changed callback.
94 osl::Condition m_aDocumentSizeCondition;
95 xmlBufferPtr m_pXmlBuffer;
96 TestLokCallbackWrapper m_callbackWrapper;
99 SdTiledRenderingTest::SdTiledRenderingTest()
100 : UnoApiXmlTest("/sd/qa/unit/tiledrendering/data/"),
101 m_bFound(true),
102 m_nPart(0),
103 m_nSelectionBeforeSearchResult(0),
104 m_nSelectionAfterSearchResult(0),
105 m_pXmlBuffer(nullptr),
106 m_callbackWrapper(&callback, this)
110 void SdTiledRenderingTest::setUp()
112 UnoApiXmlTest::setUp();
114 // prevent showing warning message box
115 setenv("OOX_NO_SMARTART_WARNING", "1", 1);
116 comphelper::LibreOfficeKit::setActive(true);
119 void SdTiledRenderingTest::tearDown()
121 if (mxComponent.is())
123 mxComponent->dispose();
124 mxComponent.clear();
127 if (m_pXmlBuffer)
128 xmlBufferFree(m_pXmlBuffer);
130 m_callbackWrapper.clear();
131 comphelper::LibreOfficeKit::setActive(false);
133 UnoApiXmlTest::tearDown();
136 SdXImpressDocument* SdTiledRenderingTest::createDoc(const char* pName, const uno::Sequence<beans::PropertyValue>& rArguments)
138 loadFromURL(OUString::createFromAscii(pName));
139 SdXImpressDocument* pImpressDocument = dynamic_cast<SdXImpressDocument*>(mxComponent.get());
140 CPPUNIT_ASSERT(pImpressDocument);
141 pImpressDocument->initializeForTiledRendering(rArguments);
142 return pImpressDocument;
145 void SdTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell& pViewShell)
147 pViewShell.setLibreOfficeKitViewCallback(&m_callbackWrapper);
148 m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(&pViewShell));
151 void SdTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
153 static_cast<SdTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
156 namespace
159 std::vector<OUString> lcl_convertSeparated(std::u16string_view rString, sal_Unicode nSeparator)
161 std::vector<OUString> aRet;
163 sal_Int32 nIndex = 0;
166 OUString aToken( o3tl::trim(o3tl::getToken(rString, 0, nSeparator, nIndex)) );
167 if (!aToken.isEmpty())
168 aRet.push_back(aToken);
170 while (nIndex >= 0);
172 return aRet;
175 void lcl_convertRectangle(std::u16string_view rString, ::tools::Rectangle& rRectangle)
177 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(rString);
178 CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
179 rRectangle.SetLeft(aSeq[0].toInt32());
180 rRectangle.SetTop(aSeq[1].toInt32());
181 rRectangle.setWidth(aSeq[2].toInt32());
182 rRectangle.setHeight(aSeq[3].toInt32());
185 } // end anonymous namespace
187 void SdTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
189 switch (nType)
191 case LOK_CALLBACK_INVALIDATE_TILES:
193 OUString aPayload = OUString::createFromAscii(pPayload);
194 if (aPayload != "EMPTY" && m_aInvalidation.IsEmpty())
195 lcl_convertRectangle(aPayload, m_aInvalidation);
197 break;
198 case LOK_CALLBACK_TEXT_SELECTION:
200 OUString aPayload = OUString::createFromAscii(pPayload);
201 m_aSelection.clear();
202 for (const OUString& rString : lcl_convertSeparated(aPayload, u';'))
204 ::tools::Rectangle aRectangle;
205 lcl_convertRectangle(rString, aRectangle);
206 m_aSelection.push_back(aRectangle);
208 if (m_aSearchResultSelection.empty())
209 ++m_nSelectionBeforeSearchResult;
210 else
211 ++m_nSelectionAfterSearchResult;
213 break;
214 case LOK_CALLBACK_SEARCH_NOT_FOUND:
216 m_bFound = false;
218 break;
219 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
221 m_aDocumentSizeCondition.set();
223 break;
224 case LOK_CALLBACK_SET_PART:
226 OUString aPayload = OUString::createFromAscii(pPayload);
227 m_nPart = aPayload.toInt32();
229 break;
230 case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
232 m_aSearchResultSelection.clear();
233 m_aSearchResultPart.clear();
234 boost::property_tree::ptree aTree;
235 std::stringstream aStream(pPayload);
236 boost::property_tree::read_json(aStream, aTree);
237 for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("searchResultSelection"))
239 m_aSearchResultSelection.emplace_back(rValue.second.get<std::string>("rectangles").c_str());
240 m_aSearchResultPart.push_back(std::atoi(rValue.second.get<std::string>("part").c_str()));
243 break;
247 xmlDocUniquePtr SdTiledRenderingTest::parseXmlDump()
249 if (m_pXmlBuffer)
250 xmlBufferFree(m_pXmlBuffer);
252 // Create the xml writer.
253 m_pXmlBuffer = xmlBufferCreate();
254 xmlTextWriterPtr pXmlWriter = xmlNewTextWriterMemory(m_pXmlBuffer, 0);
255 (void)xmlTextWriterStartDocument(pXmlWriter, nullptr, nullptr, nullptr);
257 // Create the dump.
258 SdXImpressDocument* pImpressDocument = dynamic_cast<SdXImpressDocument*>(mxComponent.get());
259 CPPUNIT_ASSERT(pImpressDocument);
260 pImpressDocument->GetDoc()->dumpAsXml(pXmlWriter);
262 // Delete the xml writer.
263 (void)xmlTextWriterEndDocument(pXmlWriter);
264 xmlFreeTextWriter(pXmlWriter);
266 auto pCharBuffer = xmlBufferContent(m_pXmlBuffer);
267 SAL_INFO("test", "SdTiledRenderingTest::parseXmlDump: pCharBuffer is '" << pCharBuffer << "'");
268 return xmlDocUniquePtr(xmlParseDoc(pCharBuffer));
271 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCreateDestroy)
273 createDoc("dummy.odp");
274 // Nothing to do, the tearDown call should cleanup.
277 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCreateView)
279 createDoc("dummy.odp");
281 SfxLokHelper::createView();
284 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testRegisterCallback)
286 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
287 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
288 setupLibreOfficeKitViewCallback(pViewShell->GetViewShellBase());
290 // Start text edit of the empty title shape.
291 SdPage* pActualPage = pViewShell->GetActualPage();
292 SdrObject* pObject = pActualPage->GetObj(0);
293 SdrView* pView = pViewShell->GetView();
294 pView->SdrBeginTextEdit(pObject);
295 CPPUNIT_ASSERT(pView->GetTextEditObject());
297 // Check that the top left 256x256px tile would be invalidated.
298 CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
299 ::tools::Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
300 CPPUNIT_ASSERT(m_aInvalidation.Overlaps(aTopLeft));
303 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPostKeyEvent)
305 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
306 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
307 SdPage* pActualPage = pViewShell->GetActualPage();
308 SdrObject* pObject = pActualPage->GetObj(0);
309 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject->GetObjIdentifier());
310 SdrTextObj* pTextObj = static_cast<SdrTextObj*>(pObject);
311 SdrView* pView = pViewShell->GetView();
312 pView->MarkObj(pTextObj, pView->GetSdrPageView());
313 SfxStringItem aInputString(SID_ATTR_CHAR, "x");
314 pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR,
315 SfxCallMode::SYNCHRON, { &aInputString });
317 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
318 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
319 Scheduler::ProcessEventsToIdle();
321 CPPUNIT_ASSERT(pView->GetTextEditObject());
322 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
323 // Did we manage to enter a second character?
324 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), rEditView.GetSelection().nStartPos);
325 ESelection aWordSelection(0, 0, 0, 2); // start para, start char, end para, end char.
326 rEditView.SetSelection(aWordSelection);
327 // Did we enter the expected character?
328 CPPUNIT_ASSERT_EQUAL(OUString("xx"), rEditView.GetSelected());
331 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPostMouseEvent)
333 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
334 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
335 SdPage* pActualPage = pViewShell->GetActualPage();
336 SdrObject* pObject = pActualPage->GetObj(0);
337 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject->GetObjIdentifier());
338 SdrTextObj* pTextObj = static_cast<SdrTextObj*>(pObject);
339 SdrView* pView = pViewShell->GetView();
340 pView->MarkObj(pTextObj, pView->GetSdrPageView());
341 SfxStringItem aInputString(SID_ATTR_CHAR, "x");
342 pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR,
343 SfxCallMode::SYNCHRON, { &aInputString });
344 CPPUNIT_ASSERT(pView->GetTextEditObject());
345 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
346 // Did we manage to go after the first character?
347 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), rEditView.GetSelection().nStartPos);
349 vcl::Cursor* pCursor = rEditView.GetCursor();
350 Point aPosition(pCursor->GetPos().getX(), pCursor->GetPos().getY() + pCursor->GetSize().Height() / 2);
351 aPosition.setX(aPosition.getX() - 1000);
352 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
353 o3tl::toTwips(aPosition.getX(), o3tl::Length::mm100), o3tl::toTwips(aPosition.getY(), o3tl::Length::mm100),
354 1, MOUSE_LEFT, 0);
355 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
356 o3tl::toTwips(aPosition.getX(), o3tl::Length::mm100), o3tl::toTwips(aPosition.getY(), o3tl::Length::mm100),
357 1, MOUSE_LEFT, 0);
358 Scheduler::ProcessEventsToIdle();
359 CPPUNIT_ASSERT(pView->GetTextEditObject());
360 // The new cursor position must be before the first word.
361 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), rEditView.GetSelection().nStartPos);
364 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSetTextSelection)
366 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
367 uno::Reference<container::XIndexAccess> xDrawPage(pXImpressDocument->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
368 uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
369 xShape->setString("Aaa bbb.");
370 // Create a selection on the second word.
371 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
372 SdPage* pActualPage = pViewShell->GetActualPage();
373 SdrObject* pObject = pActualPage->GetObj(0);
374 SdrView* pView = pViewShell->GetView();
375 pView->SdrBeginTextEdit(pObject);
376 CPPUNIT_ASSERT(pView->GetTextEditObject());
377 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
378 ESelection aWordSelection(0, 4, 0, 7);
379 rEditView.SetSelection(aWordSelection);
380 // Did we indeed manage to select the second word?
381 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), rEditView.GetSelected());
383 // Now use setTextSelection() to move the end of the selection 1000 twips right.
384 vcl::Cursor* pCursor = rEditView.GetCursor();
385 Point aEnd = pCursor->GetPos();
386 aEnd.setX(aEnd.getX() + 1000);
387 pXImpressDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aEnd.getX(), aEnd.getY());
388 // The new selection must include the ending dot, too -- but not the first word.
389 CPPUNIT_ASSERT_EQUAL(OUString("bbb."), rEditView.GetSelected());
392 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testGetTextSelection)
394 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
395 uno::Reference<container::XIndexAccess> xDrawPage(pXImpressDocument->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
396 uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
397 xShape->setString("Shape");
398 // Create a selection on the shape text.
399 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
400 SdPage* pActualPage = pViewShell->GetActualPage();
401 SdrObject* pObject = pActualPage->GetObj(0);
402 SdrView* pView = pViewShell->GetView();
403 pView->SdrBeginTextEdit(pObject);
404 CPPUNIT_ASSERT(pView->GetTextEditObject());
405 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
406 ESelection aWordSelection(0, 0, 0, 5);
407 rEditView.SetSelection(aWordSelection);
408 // Did we indeed manage to copy the selected text?
409 CPPUNIT_ASSERT_EQUAL(OString("Shape"), apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));
411 // Make sure returned RTF is not empty.
412 CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/rtf").isEmpty());
415 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSetGraphicSelection)
417 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
418 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
419 SdPage* pPage = pViewShell->GetActualPage();
420 SdrObject* pObject = pPage->GetObj(0);
421 SdrHdlList handleList(nullptr);
422 pObject->AddToHdlList(handleList);
423 // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
424 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8), handleList.GetHdlCount());
425 // Take the bottom center one.
426 SdrHdl* pHdl = handleList.GetHdl(6);
427 CPPUNIT_ASSERT_EQUAL(int(SdrHdlKind::Lower), static_cast<int>(pHdl->GetKind()));
428 ::tools::Rectangle aShapeBefore = pObject->GetSnapRect();
429 // Resize.
430 pXImpressDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, o3tl::toTwips(pHdl->GetPos().getX(), o3tl::Length::mm100), o3tl::toTwips(pHdl->GetPos().getY(), o3tl::Length::mm100));
431 pXImpressDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, o3tl::toTwips(pHdl->GetPos().getX(), o3tl::Length::mm100), o3tl::toTwips(pHdl->GetPos().getY() + 1000, o3tl::Length::mm100));
433 // Assert that view shell ID tracking works.
434 sal_Int32 nView1 = SfxLokHelper::getView();
435 SdDrawDocument* pDocument = pXImpressDocument->GetDoc();
436 sd::UndoManager* pUndoManager = pDocument->GetUndoManager();
437 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pUndoManager->GetUndoActionCount());
438 auto pListAction = dynamic_cast<SfxListUndoAction*>(pUndoManager->GetUndoAction());
439 CPPUNIT_ASSERT(pListAction);
440 for (size_t i = 0; i < pListAction->maUndoActions.size(); ++i)
441 // The second item was -1 here, view shell ID wasn't known.
442 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), pListAction->GetUndoAction(i)->GetViewShellId());
444 ::tools::Rectangle aShapeAfter = pObject->GetSnapRect();
445 // Check that a resize happened, but aspect ratio is not kept.
446 CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenWidth(), aShapeAfter.getOpenWidth());
447 CPPUNIT_ASSERT(aShapeBefore.getOpenHeight() < aShapeAfter.getOpenHeight());
450 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testUndoShells)
452 // Load a document and set the page size.
453 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
454 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
456 {"AttributePageSize.Width", uno::Any(static_cast<sal_Int32>(10000))},
457 {"AttributePageSize.Height", uno::Any(static_cast<sal_Int32>(10000))},
458 }));
459 dispatchCommand(mxComponent, ".uno:AttributePageSize", aPropertyValues);
461 // Assert that view shell ID tracking works for SdUndoAction subclasses.
462 SdDrawDocument* pDocument = pXImpressDocument->GetDoc();
463 sd::UndoManager* pUndoManager = pDocument->GetUndoManager();
464 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pUndoManager->GetUndoActionCount());
465 sal_Int32 nView1 = SfxLokHelper::getView();
466 // This was -1, SdUndoGroup did not track what view shell created it.
467 CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), pUndoManager->GetUndoAction()->GetViewShellId());
470 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testResetSelection)
472 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
473 uno::Reference<container::XIndexAccess> xDrawPage(pXImpressDocument->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
474 uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
475 xShape->setString("Aaa bbb.");
476 // Create a selection on the second word.
477 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
478 SdPage* pActualPage = pViewShell->GetActualPage();
479 SdrObject* pObject = pActualPage->GetObj(0);
480 SdrView* pView = pViewShell->GetView();
481 pView->SdrBeginTextEdit(pObject);
482 CPPUNIT_ASSERT(pView->GetTextEditObject());
483 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
484 ESelection aWordSelection(0, 4, 0, 7);
485 rEditView.SetSelection(aWordSelection);
486 // Did we indeed manage to select the second word?
487 CPPUNIT_ASSERT_EQUAL(OUString("bbb"), rEditView.GetSelected());
489 // Now use resetSelection() to reset the selection.
490 pXImpressDocument->resetSelection();
491 CPPUNIT_ASSERT(!pView->GetTextEditObject());
494 namespace
497 std::vector<OUString> getCurrentParts(SdXImpressDocument* pDocument)
499 int parts = pDocument->getParts();
500 std::vector<OUString> result;
502 result.reserve(parts);
503 for (int i = 0; i < parts; i++)
505 result.push_back(pDocument->getPartName(i));
508 return result;
513 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testInsertDeletePage)
515 SdXImpressDocument* pXImpressDocument = createDoc("insert-delete.odp");
516 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
517 setupLibreOfficeKitViewCallback(pViewShell->GetViewShellBase());
519 SdDrawDocument* pDoc = pXImpressDocument->GetDocShell()->GetDoc();
520 CPPUNIT_ASSERT(pDoc);
522 std::vector<OUString> aInserted =
524 "Slide 1", "Slide 2", "Slide 3", "Slide 4", "Slide 5",
525 "Slide 6", "Slide 7", "Slide 8", "Slide 9", "Slide 10", "Slide 11"
528 std::vector<OUString> aDeleted =
530 "Slide 1"
533 // the document has 1 slide
534 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), pDoc->GetSdPageCount(PageKind::Standard));
536 uno::Sequence<beans::PropertyValue> aArgs;
538 // Insert slides
539 m_aDocumentSizeCondition.reset();
540 for (unsigned it = 1; it <= 10; it++)
541 dispatchCommand(mxComponent, ".uno:InsertPage", aArgs);
543 osl::Condition::Result aResult = m_aDocumentSizeCondition.wait(std::chrono::seconds(2));
544 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
546 // Verify inserted slides
547 std::vector<OUString> aPageList(getCurrentParts(pXImpressDocument));
548 CPPUNIT_ASSERT_EQUAL(aPageList.size(), aInserted.size());
550 for (auto it1 = aPageList.begin(), it2 = aInserted.begin(); it1 != aPageList.end(); ++it1, ++it2)
552 CPPUNIT_ASSERT_EQUAL(*it1, *it2);
555 // Delete slides
556 m_aDocumentSizeCondition.reset();
557 for (unsigned it = 1; it <= 10; it++)
558 dispatchCommand(mxComponent, ".uno:DeletePage", aArgs);
560 aResult = m_aDocumentSizeCondition.wait(std::chrono::seconds(2));
561 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
563 // Verify deleted slides
564 aPageList = getCurrentParts(pXImpressDocument);
565 CPPUNIT_ASSERT_EQUAL(aPageList.size(), aDeleted.size());
566 for (auto it1 = aPageList.begin(), it2 = aDeleted.begin(); it1 != aPageList.end(); ++it1, ++it2)
568 CPPUNIT_ASSERT_EQUAL(*it1, *it2);
571 // Undo deleted slides
572 m_aDocumentSizeCondition.reset();
573 for (unsigned it = 1; it <= 10; it++)
574 dispatchCommand(mxComponent, ".uno:Undo", aArgs);
576 aResult = m_aDocumentSizeCondition.wait(std::chrono::seconds(2));
577 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
579 // Verify inserted slides
580 aPageList = getCurrentParts(pXImpressDocument);
581 CPPUNIT_ASSERT_EQUAL(aPageList.size(), aInserted.size());
582 for (auto it1 = aPageList.begin(), it2 = aInserted.begin(); it1 != aPageList.end(); ++it1, ++it2)
584 CPPUNIT_ASSERT_EQUAL(*it1, *it2);
587 // Redo deleted slides
588 m_aDocumentSizeCondition.reset();
589 for (unsigned it = 1; it <= 10; it++)
590 dispatchCommand(mxComponent, ".uno:Redo", aArgs);
592 aResult = m_aDocumentSizeCondition.wait(std::chrono::seconds(2));
593 CPPUNIT_ASSERT_EQUAL(osl::Condition::result_ok, aResult);
595 // Verify deleted slides
596 aPageList = getCurrentParts(pXImpressDocument);
597 CPPUNIT_ASSERT_EQUAL(aPageList.size(), aDeleted.size());
598 for (auto it1 = aPageList.begin(), it2 = aDeleted.begin(); it1 != aPageList.end(); ++it1, ++it2)
600 CPPUNIT_ASSERT_EQUAL(*it1, *it2);
603 // the document has 1 slide
604 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), pDoc->GetSdPageCount(PageKind::Standard));
607 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testInsertTable)
609 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
611 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
613 { "Rows", uno::Any(sal_Int32(3)) },
614 { "Columns", uno::Any(sal_Int32(5)) }
615 }));
617 dispatchCommand(mxComponent, ".uno:InsertTable", aArgs);
619 // get the table
620 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
621 SdPage* pActualPage = pViewShell->GetActualPage();
622 SdrObject* pObject = pActualPage->GetObj(1);
623 CPPUNIT_ASSERT(pObject);
625 // check that the table is not in the top left corner
626 Point aPos(pObject->GetRelativePos());
628 CPPUNIT_ASSERT(aPos.X() != 0);
629 CPPUNIT_ASSERT(aPos.Y() != 0);
632 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testDeleteTable)
634 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
636 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
638 { "Rows", uno::Any(sal_Int32(3)) },
639 { "Columns", uno::Any(sal_Int32(5)) }
640 }));
642 dispatchCommand(mxComponent, ".uno:InsertTable", aArgs);
643 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
644 SdrView* pSdrView = pViewShell->GetView();
645 const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
646 CPPUNIT_ASSERT(rMarkList.GetMarkCount());
647 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_MOD1 | awt::Key::A);
648 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_MOD1 | awt::Key::A);
649 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
650 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
651 Scheduler::ProcessEventsToIdle();
652 CPPUNIT_ASSERT(!rMarkList.GetMarkCount());
655 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPartHash)
657 SdXImpressDocument* pDoc = createDoc("dummy.odp");
659 int nParts = pDoc->getParts();
660 for (int it = 0; it < nParts; it++)
662 CPPUNIT_ASSERT(!pDoc->getPartHash(it).isEmpty());
665 // check part that it does not exists
666 CPPUNIT_ASSERT(pDoc->getPartHash(100).isEmpty());
669 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testResizeTable)
671 // Load the document.
672 SdXImpressDocument* pXImpressDocument = createDoc("table.odp");
673 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
674 SdPage* pActualPage = pViewShell->GetActualPage();
675 SdrObject* pObject = pActualPage->GetObj(0);
676 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pObject);
677 CPPUNIT_ASSERT(pTableObject);
679 // Select the table by marking it + starting and ending text edit.
680 SdrView* pView = pViewShell->GetView();
681 pView->MarkObj(pObject, pView->GetSdrPageView());
682 pView->SdrBeginTextEdit(pObject);
683 pView->SdrEndTextEdit();
685 // Remember the original row heights.
686 uno::Reference<table::XColumnRowRange> xTable = pTableObject->getTable();
687 uno::Reference<container::XIndexAccess> xRows = xTable->getRows();
688 uno::Reference<beans::XPropertySet> xRow1(xRows->getByIndex(0), uno::UNO_QUERY);
689 sal_Int32 nExpectedRow1 = xRow1->getPropertyValue("Size").get<sal_Int32>();
690 uno::Reference<beans::XPropertySet> xRow2(xRows->getByIndex(1), uno::UNO_QUERY);
691 sal_Int32 nExpectedRow2 = xRow2->getPropertyValue("Size").get<sal_Int32>();
693 // Resize the upper row, decrease its height by 1 cm.
694 Point aInnerRowEdge = pObject->GetSnapRect().Center();
695 pXImpressDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, o3tl::toTwips(aInnerRowEdge.getX(), o3tl::Length::mm100), o3tl::toTwips(aInnerRowEdge.getY(), o3tl::Length::mm100));
696 pXImpressDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, o3tl::toTwips(aInnerRowEdge.getX(), o3tl::Length::mm100), o3tl::toTwips(aInnerRowEdge.getY() - 1000, o3tl::Length::mm100));
698 // Remember the resized row heights.
699 sal_Int32 nResizedRow1 = xRow1->getPropertyValue("Size").get<sal_Int32>();
700 CPPUNIT_ASSERT(nResizedRow1 < nExpectedRow1);
701 sal_Int32 nResizedRow2 = xRow2->getPropertyValue("Size").get<sal_Int32>();
702 CPPUNIT_ASSERT_EQUAL(nExpectedRow2, nResizedRow2);
704 // Now undo the resize.
705 pXImpressDocument->GetDocShell()->GetUndoManager()->Undo();
707 // Check the undo result.
708 sal_Int32 nActualRow1 = xRow1->getPropertyValue("Size").get<sal_Int32>();
709 CPPUNIT_ASSERT_EQUAL(nExpectedRow1, nActualRow1);
710 sal_Int32 nActualRow2 = xRow2->getPropertyValue("Size").get<sal_Int32>();
711 // Expected was 4000, actual was 4572, i.e. the second row after undo was larger than expected.
712 CPPUNIT_ASSERT_EQUAL(nExpectedRow2, nActualRow2);
715 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testResizeTableColumn)
717 // Load the document.
718 SdXImpressDocument* pXImpressDocument = createDoc("table-column.odp");
719 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
720 SdPage* pActualPage = pViewShell->GetActualPage();
721 SdrObject* pObject = pActualPage->GetObj(0);
722 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pObject);
723 CPPUNIT_ASSERT(pTableObject);
725 // Select the table by marking it + starting and ending text edit.
726 SdrView* pView = pViewShell->GetView();
727 pView->MarkObj(pObject, pView->GetSdrPageView());
728 pView->SdrBeginTextEdit(pObject);
729 pView->SdrEndTextEdit();
731 // Remember the original cell widths.
732 xmlDocUniquePtr pXmlDoc = parseXmlDump();
733 OString aPrefix = "/SdDrawDocument/SdrModel/maPages/SdPage/SdrPage/SdrObjList/SdrTableObj/SdrTableObjImpl/TableLayouter/columns/";
734 sal_Int32 nExpectedColumn1 = getXPath(pXmlDoc, aPrefix + "TableLayouter_Layout[1]", "size").toInt32();
735 sal_Int32 nExpectedColumn2 = getXPath(pXmlDoc, aPrefix + "TableLayouter_Layout[2]", "size").toInt32();
736 pXmlDoc = nullptr;
738 // Resize the left column, decrease its width by 1 cm.
739 Point aInnerRowEdge = pObject->GetSnapRect().Center();
740 pXImpressDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, o3tl::toTwips(aInnerRowEdge.getX(), o3tl::Length::mm100), o3tl::toTwips(aInnerRowEdge.getY(), o3tl::Length::mm100));
741 pXImpressDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, o3tl::toTwips(aInnerRowEdge.getX() - 1000, o3tl::Length::mm100), o3tl::toTwips(aInnerRowEdge.getY(), o3tl::Length::mm100));
743 // Remember the resized column widths.
744 pXmlDoc = parseXmlDump();
745 sal_Int32 nResizedColumn1 = getXPath(pXmlDoc, aPrefix + "TableLayouter_Layout[1]", "size").toInt32();
746 CPPUNIT_ASSERT(nResizedColumn1 < nExpectedColumn1);
747 sal_Int32 nResizedColumn2 = getXPath(pXmlDoc, aPrefix + "TableLayouter_Layout[2]", "size").toInt32();
748 CPPUNIT_ASSERT(nResizedColumn2 > nExpectedColumn2);
749 pXmlDoc = nullptr;
751 // Now undo the resize.
752 pXImpressDocument->GetDocShell()->GetUndoManager()->Undo();
754 // Check the undo result.
755 pXmlDoc = parseXmlDump();
756 sal_Int32 nActualColumn1 = getXPath(pXmlDoc, aPrefix + "TableLayouter_Layout[1]", "size").toInt32();
757 // Expected was 7049, actual was 6048, i.e. the first column width after undo was 1cm smaller than expected.
758 CPPUNIT_ASSERT_EQUAL(nExpectedColumn1, nActualColumn1);
759 sal_Int32 nActualColumn2 = getXPath(pXmlDoc, aPrefix + "TableLayouter_Layout[2]", "size").toInt32();
760 CPPUNIT_ASSERT_EQUAL(nExpectedColumn2, nActualColumn2);
761 pXmlDoc = nullptr;
764 namespace {
766 /// A view callback tracks callbacks invoked on one specific view.
767 class ViewCallback final
769 SfxViewShell* mpViewShell;
770 int mnView;
771 public:
772 bool m_bGraphicSelectionInvalidated;
773 bool m_bGraphicViewSelectionInvalidated;
774 /// Our current part, to be able to decide if a view cursor/selection is relevant for us.
775 int m_nPart;
776 bool m_bCursorVisibleChanged;
777 bool m_bCursorVisible;
778 bool m_bViewLock;
779 bool m_bTilesInvalidated;
780 std::vector<tools::Rectangle> m_aInvalidations;
781 std::map<int, bool> m_aViewCursorInvalidations;
782 std::map<int, bool> m_aViewCursorVisibilities;
783 bool m_bViewSelectionSet;
784 boost::property_tree::ptree m_aCommentCallbackResult;
785 OString m_ShapeSelection;
786 TestLokCallbackWrapper m_callbackWrapper;
788 ViewCallback()
789 : m_bGraphicSelectionInvalidated(false),
790 m_bGraphicViewSelectionInvalidated(false),
791 m_nPart(0),
792 m_bCursorVisibleChanged(false),
793 m_bCursorVisible(false),
794 m_bViewLock(false),
795 m_bTilesInvalidated(false),
796 m_bViewSelectionSet(false),
797 m_callbackWrapper(&callback, this)
799 mpViewShell = SfxViewShell::Current();
800 mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
801 mnView = SfxLokHelper::getView();
802 m_callbackWrapper.setLOKViewId( mnView );
805 ~ViewCallback()
807 SfxLokHelper::setView(mnView);
808 mpViewShell->setLibreOfficeKitViewCallback(nullptr);
811 static void callback(int nType, const char* pPayload, void* pData)
813 static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
816 void callbackImpl(int nType, const char* pPayload)
818 switch (nType)
820 case LOK_CALLBACK_INVALIDATE_TILES:
822 m_bTilesInvalidated = true;
823 OString text(pPayload);
824 if (!text.startsWith("EMPTY"))
826 uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
827 CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
828 tools::Rectangle aInvalidationRect;
829 aInvalidationRect.SetLeft(aSeq[0].toInt32());
830 aInvalidationRect.SetTop(aSeq[1].toInt32());
831 aInvalidationRect.setWidth(aSeq[2].toInt32());
832 aInvalidationRect.setHeight(aSeq[3].toInt32());
833 m_aInvalidations.push_back(aInvalidationRect);
836 break;
837 case LOK_CALLBACK_GRAPHIC_SELECTION:
839 m_bGraphicSelectionInvalidated = true;
840 m_ShapeSelection = OString(pPayload);
842 break;
843 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
845 std::stringstream aStream(pPayload);
846 boost::property_tree::ptree aTree;
847 boost::property_tree::read_json(aStream, aTree);
848 if (aTree.get_child("part").get_value<int>() == m_nPart)
849 // Ignore callbacks which are for a different part.
850 m_bGraphicViewSelectionInvalidated = true;
852 break;
853 case LOK_CALLBACK_CURSOR_VISIBLE:
855 m_bCursorVisibleChanged = true;
856 m_bCursorVisible = (std::string_view("true") == pPayload);
858 break;
859 case LOK_CALLBACK_VIEW_LOCK:
861 std::stringstream aStream(pPayload);
862 boost::property_tree::ptree aTree;
863 boost::property_tree::read_json(aStream, aTree);
864 m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
866 break;
867 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
869 std::stringstream aStream(pPayload);
870 boost::property_tree::ptree aTree;
871 boost::property_tree::read_json(aStream, aTree);
872 int nViewId = aTree.get_child("viewId").get_value<int>();
873 m_aViewCursorInvalidations[nViewId] = true;
875 break;
876 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
878 std::stringstream aStream(pPayload);
879 boost::property_tree::ptree aTree;
880 boost::property_tree::read_json(aStream, aTree);
881 const int nViewId = aTree.get_child("viewId").get_value<int>();
882 m_aViewCursorVisibilities[nViewId] = std::string_view("true") == pPayload;
884 break;
885 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
887 m_bViewSelectionSet = true;
889 break;
890 case LOK_CALLBACK_COMMENT:
892 m_aCommentCallbackResult.clear();
893 std::stringstream aStream(pPayload);
894 boost::property_tree::read_json(aStream, m_aCommentCallbackResult);
895 m_aCommentCallbackResult = m_aCommentCallbackResult.get_child("comment");
897 break;
904 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testViewCursors)
906 // Create two views.
907 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
908 ViewCallback aView1;
909 SfxLokHelper::createView();
910 ViewCallback aView2;
912 // Select the shape in the second view.
913 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
914 SdPage* pActualPage = pViewShell->GetActualPage();
915 SdrObject* pObject = pActualPage->GetObj(0);
916 SdrView* pView = pViewShell->GetView();
917 pView->MarkObj(pObject, pView->GetSdrPageView());
918 Scheduler::ProcessEventsToIdle();
920 // First view notices that there was a selection change in the other view.
921 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelectionInvalidated);
922 // Second view notices that there was a selection change in its own view.
923 CPPUNIT_ASSERT(aView2.m_bGraphicSelectionInvalidated);
926 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testViewCursorParts)
928 // Create two views.
929 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
930 ViewCallback aView1;
931 SfxLokHelper::createView();
932 pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
933 ViewCallback aView2;
935 // Select the shape in the second view.
936 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
937 SdPage* pActualPage = pViewShell->GetActualPage();
938 SdrObject* pObject = pActualPage->GetObj(0);
939 SdrView* pView = pViewShell->GetView();
940 pView->MarkObj(pObject, pView->GetSdrPageView());
941 Scheduler::ProcessEventsToIdle();
942 // First view notices that there was a selection change in the other view.
943 CPPUNIT_ASSERT(aView1.m_bGraphicViewSelectionInvalidated);
944 pView->UnmarkAllObj(pView->GetSdrPageView());
946 // Now switch to the second part in the second view.
947 pXImpressDocument->setPart(1);
948 aView2.m_nPart = 1;
949 aView1.m_bGraphicViewSelectionInvalidated = false;
950 pActualPage = pViewShell->GetActualPage();
951 pObject = pActualPage->GetObj(0);
952 pView->MarkObj(pObject, pView->GetSdrPageView());
953 Scheduler::ProcessEventsToIdle();
954 // First view ignores view selection, as it would be for part 1, and it's in part 0.
955 // This failed when the "part" was always 0 in the callback.
956 CPPUNIT_ASSERT(!aView1.m_bGraphicViewSelectionInvalidated);
959 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCursorViews)
961 // Create the first view.
962 SdXImpressDocument* pXImpressDocument = createDoc("title-shape.odp");
963 int nView1 = SfxLokHelper::getView();
964 ViewCallback aView1;
966 // Begin text edit on the only object on the slide.
967 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
968 SdrView* pView = pViewShell->GetView();
969 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
970 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
971 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
972 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
973 Scheduler::ProcessEventsToIdle();
974 CPPUNIT_ASSERT(pView->IsTextEdit());
976 // Make sure that cursor state is not changed just because we create a second view.
977 aView1.m_bCursorVisibleChanged = false;
978 SfxLokHelper::createView();
979 pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
980 Scheduler::ProcessEventsToIdle();
981 CPPUNIT_ASSERT(!aView1.m_bCursorVisibleChanged);
983 // Make sure that typing in the first view causes an invalidation in the
984 // second view as well, even if the second view was created after begin
985 // text edit in the first view.
986 ViewCallback aView2;
987 // This failed: the second view didn't get a lock notification, even if the
988 // first view already started text edit.
989 CPPUNIT_ASSERT(aView2.m_bViewLock);
990 SfxLokHelper::setView(nView1);
991 aView2.m_bTilesInvalidated = false;
992 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
993 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
994 Scheduler::ProcessEventsToIdle();
995 // This failed: the second view was not invalidated when pressing a key in
996 // the first view.
997 CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
1000 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCursorVisibility_SingleClick)
1002 // Single-clicking in a text box enters editing only
1003 // when it's on the text, even if it's the default text.
1005 // Load doc.
1006 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1007 ViewCallback aView1;
1009 // Begin text edit on the only object on the slide.
1010 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1011 SdPage* pActualPage = pViewShell->GetActualPage();
1012 SdrObject* pObject1 = pActualPage->GetObj(0);
1013 CPPUNIT_ASSERT(pObject1 != nullptr);
1014 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject1->GetObjIdentifier());
1015 SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
1017 // Click once outside of the text (in the first quartile) => no editing.
1018 const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
1019 const auto cornerX = o3tl::toTwips(aRect.Left() + (aRect.getOpenWidth() / 4), o3tl::Length::mm100);
1020 const auto cornerY = o3tl::toTwips(aRect.Top() + (aRect.getOpenHeight() / 4), o3tl::Length::mm100);
1021 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1022 cornerX, cornerY,
1023 1, MOUSE_LEFT, 0);
1024 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1025 cornerX, cornerY,
1026 1, MOUSE_LEFT, 0);
1027 Scheduler::ProcessEventsToIdle();
1029 // No editing.
1030 CPPUNIT_ASSERT(!pViewShell->GetView()->IsTextEdit());
1031 CPPUNIT_ASSERT(!aView1.m_bCursorVisible);
1033 // Click again, now on the text, in the center, to start editing.
1034 const auto centerX = o3tl::toTwips(aRect.Left() + (aRect.getOpenWidth() / 2), o3tl::Length::mm100);
1035 const auto centerY = o3tl::toTwips(aRect.Top() + (aRect.getOpenHeight() / 2), o3tl::Length::mm100);
1036 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1037 centerX, centerY,
1038 1, MOUSE_LEFT, 0);
1039 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1040 centerX, centerY,
1041 1, MOUSE_LEFT, 0);
1042 Scheduler::ProcessEventsToIdle();
1044 // We must be in text editing mode and have cursor visible.
1045 CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
1046 CPPUNIT_ASSERT(aView1.m_bCursorVisible);
1050 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCursorVisibility_DoubleClick)
1052 // Double-clicking anywhere in the TextBox should start editing.
1054 // Create the first view.
1055 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1056 ViewCallback aView1;
1058 // Begin text edit on the only object on the slide.
1059 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1060 SdPage* pActualPage = pViewShell->GetActualPage();
1061 SdrObject* pObject1 = pActualPage->GetObj(0);
1062 CPPUNIT_ASSERT(pObject1 != nullptr);
1063 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject1->GetObjIdentifier());
1064 SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
1066 // Double-click outside the text to enter edit mode.
1067 const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
1068 const auto cornerX = o3tl::toTwips(aRect.Left() + (aRect.getOpenWidth() / 4), o3tl::Length::mm100);
1069 const auto cornerY = o3tl::toTwips(aRect.Top() + (aRect.getOpenHeight() / 4), o3tl::Length::mm100);
1070 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1071 cornerX, cornerY,
1072 2, MOUSE_LEFT, 0);
1073 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1074 cornerX, cornerY,
1075 2, MOUSE_LEFT, 0);
1076 Scheduler::ProcessEventsToIdle();
1078 // We must be in text editing mode and have cursor visible.
1079 CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
1080 CPPUNIT_ASSERT(aView1.m_bCursorVisible);
1083 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCursorVisibility_MultiView)
1085 // Create the first view.
1086 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1087 const int nView1 = SfxLokHelper::getView();
1088 ViewCallback aView1;
1090 // Begin text edit on the only object on the slide.
1091 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1092 SdPage* pActualPage = pViewShell->GetActualPage();
1093 SdrObject* pObject1 = pActualPage->GetObj(0);
1094 CPPUNIT_ASSERT(pObject1);
1095 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject1->GetObjIdentifier());
1096 SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
1098 // Make sure that cursor state is not changed just because we create a second view.
1099 SfxLokHelper::createView();
1100 pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1101 const int nView2 = SfxLokHelper::getView();
1102 Scheduler::ProcessEventsToIdle();
1103 CPPUNIT_ASSERT_EQUAL(false, aView1.m_bCursorVisibleChanged);
1104 CPPUNIT_ASSERT_EQUAL(false, aView1.m_aViewCursorVisibilities[nView2]);
1106 // Also check that the second view gets the notifications.
1107 ViewCallback aView2;
1109 SfxLokHelper::setView(nView1);
1111 ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
1112 const auto centerX = o3tl::toTwips(aRect.Left() + (aRect.getOpenWidth() / 2), o3tl::Length::mm100);
1113 const auto centerY = o3tl::toTwips(aRect.Top() + (aRect.getOpenHeight() / 2), o3tl::Length::mm100);
1114 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1115 centerX, centerY,
1116 2, MOUSE_LEFT, 0);
1117 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1118 centerX, centerY,
1119 2, MOUSE_LEFT, 0);
1120 Scheduler::ProcessEventsToIdle();
1122 // We must be in text editing mode and have cursor visible.
1123 CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
1124 CPPUNIT_ASSERT(aView1.m_bCursorVisible);
1125 CPPUNIT_ASSERT_EQUAL(false, aView1.m_aViewCursorVisibilities[nView2]);
1127 CPPUNIT_ASSERT_EQUAL(false, aView2.m_bCursorVisible);
1128 CPPUNIT_ASSERT_EQUAL(false, aView2.m_aViewCursorVisibilities[nView1]);
1129 CPPUNIT_ASSERT_EQUAL(false, aView2.m_aViewCursorVisibilities[nView2]);
1132 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCursorVisibility_Escape)
1134 // Load doc.
1135 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1136 ViewCallback aView1;
1138 // Begin text edit on the only object on the slide.
1139 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1140 SdPage* pActualPage = pViewShell->GetActualPage();
1141 SdrObject* pObject1 = pActualPage->GetObj(0);
1142 CPPUNIT_ASSERT(pObject1 != nullptr);
1143 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject1->GetObjIdentifier());
1144 SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
1146 // Click once on the text to start editing.
1147 const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
1148 const auto centerX = o3tl::toTwips(aRect.Left() + (aRect.getOpenWidth() / 2), o3tl::Length::mm100);
1149 const auto centerY = o3tl::toTwips(aRect.Top() + (aRect.getOpenHeight() / 2), o3tl::Length::mm100);
1150 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1151 centerX, centerY,
1152 1, MOUSE_LEFT, 0);
1153 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1154 centerX, centerY,
1155 1, MOUSE_LEFT, 0);
1156 Scheduler::ProcessEventsToIdle();
1158 // We must be in text editing mode and have cursor visible.
1159 CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
1160 CPPUNIT_ASSERT(aView1.m_bCursorVisible);
1162 // End editing by pressing the escape key.
1163 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
1164 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
1165 Scheduler::ProcessEventsToIdle();
1167 // We must be in text editing mode and have cursor visible.
1168 CPPUNIT_ASSERT(!pViewShell->GetView()->IsTextEdit());
1169 CPPUNIT_ASSERT_EQUAL(false, aView1.m_bCursorVisible);
1172 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testViewLock)
1174 // Load a document that has a shape and create two views.
1175 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
1176 ViewCallback aView1;
1177 SfxLokHelper::createView();
1178 pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
1180 // Begin text edit in the second view and assert that the first gets a lock
1181 // notification.
1182 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1183 SdPage* pActualPage = pViewShell->GetActualPage();
1184 SdrObject* pObject = pActualPage->GetObj(0);
1185 SdrView* pView = pViewShell->GetView();
1186 aView1.m_bViewLock = false;
1187 pView->SdrBeginTextEdit(pObject);
1188 CPPUNIT_ASSERT(aView1.m_bViewLock);
1190 // End text edit in the second view, and assert that the lock is removed in
1191 // the first view.
1192 pView->SdrEndTextEdit();
1193 CPPUNIT_ASSERT(!aView1.m_bViewLock);
1196 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testUndoLimiting)
1198 // Create the first view.
1199 SdXImpressDocument* pXImpressDocument = createDoc("title-shape.odp");
1200 sd::ViewShell* pViewShell1 = pXImpressDocument->GetDocShell()->GetViewShell();
1201 int nView1 = SfxLokHelper::getView();
1202 SfxLokHelper::createView();
1203 sd::ViewShell* pViewShell2 = pXImpressDocument->GetDocShell()->GetViewShell();
1204 CPPUNIT_ASSERT(pViewShell1 != pViewShell2);
1206 // Begin text edit on the only object on the slide.
1207 SfxLokHelper::setView(nView1);
1208 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
1209 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
1210 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1211 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1212 Scheduler::ProcessEventsToIdle();
1213 CPPUNIT_ASSERT(pViewShell1->GetView()->IsTextEdit());
1215 // View2 UNDO stack should be empty
1217 SfxRequest aReq2(SID_UNDO, SfxCallMode::SLOT, pXImpressDocument->GetDocShell()->GetDoc()->GetPool());
1218 aReq2.AppendItem(SfxUInt16Item(SID_UNDO, 1));
1219 pViewShell2->ExecuteSlot(aReq2);
1220 const auto* pReturnValue = aReq2.GetReturnValue();
1221 CPPUNIT_ASSERT(!pReturnValue);
1224 // View1 can UNDO
1226 SfxRequest aReq1(SID_UNDO, SfxCallMode::SLOT, pXImpressDocument->GetDocShell()->GetDoc()->GetPool());
1227 aReq1.AppendItem(SfxUInt16Item(SID_UNDO, 1));
1228 pViewShell1->ExecuteSlot(aReq1);
1229 CPPUNIT_ASSERT(aReq1.IsDone());
1232 // View1 can REDO
1234 SfxRequest aReq1(SID_REDO, SfxCallMode::SLOT, pXImpressDocument->GetDocShell()->GetDoc()->GetPool());
1235 aReq1.AppendItem(SfxUInt16Item(SID_REDO, 1));
1236 pViewShell1->ExecuteSlot(aReq1);
1237 CPPUNIT_ASSERT(aReq1.IsDone());
1240 // Exit text edit mode
1241 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
1242 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
1243 Scheduler::ProcessEventsToIdle();
1245 CPPUNIT_ASSERT(!pViewShell1->GetView()->IsTextEdit());
1247 // Now check view2 cannot undo actions.
1249 SfxRequest aReq2(SID_UNDO, SfxCallMode::SLOT, pXImpressDocument->GetDocShell()->GetDoc()->GetPool());
1250 aReq2.AppendItem(SfxUInt16Item(SID_UNDO, 1));
1251 pViewShell2->ExecuteSlot(aReq2);
1252 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aReq2.GetReturnValue());
1253 CPPUNIT_ASSERT(pUInt32Item);
1254 CPPUNIT_ASSERT_EQUAL(static_cast< sal_uInt32 >(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
1257 // Now check view1 can undo action
1259 SfxRequest aReq1(SID_UNDO, SfxCallMode::SLOT, pXImpressDocument->GetDocShell()->GetDoc()->GetPool());
1260 aReq1.AppendItem(SfxUInt16Item(SID_UNDO, 1));
1261 pViewShell1->ExecuteSlot(aReq1);
1262 CPPUNIT_ASSERT(aReq1.IsDone());
1266 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCreateViewGraphicSelection)
1268 // Load a document and register a callback.
1269 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
1270 ViewCallback aView1;
1272 // Select the only shape in the document and assert that the graphic selection is changed.
1273 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1274 SdPage* pActualPage = pViewShell->GetActualPage();
1275 SdrObject* pObject = pActualPage->GetObj(0);
1276 SdrView* pView = pViewShell->GetView();
1277 aView1.m_bGraphicSelectionInvalidated = false;
1278 pView->MarkObj(pObject, pView->GetSdrPageView());
1279 CPPUNIT_ASSERT(aView1.m_bGraphicSelectionInvalidated);
1281 // Now create a new view.
1282 aView1.m_bGraphicSelectionInvalidated = false;
1283 SfxLokHelper::createView();
1284 pXImpressDocument->initializeForTiledRendering({});
1285 // This failed, creating a new view affected the graphic selection of an
1286 // existing view.
1287 CPPUNIT_ASSERT(!aView1.m_bGraphicSelectionInvalidated);
1289 // Check that when the first view has a shape selected and we register a
1290 // callback on the second view, then it gets a "graphic view selection".
1291 ViewCallback aView2;
1292 // This failed, the created new view had no "view selection" of the first
1293 // view's selected shape.
1294 CPPUNIT_ASSERT(aView2.m_bGraphicViewSelectionInvalidated);
1297 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCreateViewTextCursor)
1299 // Load a document and register a callback.
1300 SdXImpressDocument* pXImpressDocument = createDoc("title-shape.odp");
1301 ViewCallback aView1;
1303 // Begin text edit.
1304 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
1305 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
1306 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1307 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1308 Scheduler::ProcessEventsToIdle();
1309 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1310 SdrView* pSdrView = pViewShell->GetView();
1311 CPPUNIT_ASSERT(pSdrView->IsTextEdit());
1313 // Create an editeng text selection.
1314 EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView();
1315 // 0th para, 0th char -> 0th para, 1st char.
1316 ESelection aWordSelection(0, 0, 0, 1);
1317 rEditView.SetSelection(aWordSelection);
1319 // Make sure that creating a new view either doesn't affect the previous
1320 // one, or at least the effect is not visible at the end.
1321 aView1.m_aViewCursorInvalidations.clear();
1322 aView1.m_aViewCursorVisibilities.clear();
1323 SfxLokHelper::createView();
1324 pXImpressDocument->initializeForTiledRendering({});
1325 ViewCallback aView2;
1326 bool bFoundCursor = false;
1327 for (const auto& rInvalidation : aView1.m_aViewCursorInvalidations)
1329 auto itVisibility = aView1.m_aViewCursorVisibilities.find(rInvalidation.first);
1330 // For each cursor invalidation: if there is no visibility or the visibility is true, that's a problem.
1331 if (itVisibility == aView1.m_aViewCursorVisibilities.end() || itVisibility->second)
1333 bFoundCursor = true;
1334 break;
1337 // This failed: the second view created an unexpected view cursor in the
1338 // first view.
1339 CPPUNIT_ASSERT(!bFoundCursor);
1340 // This failed: the text view selection of the first view wasn't seen by
1341 // the second view.
1342 CPPUNIT_ASSERT(aView2.m_bViewSelectionSet);
1345 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf102223)
1347 // Load the document.
1348 SdXImpressDocument* pXImpressDocument = createDoc("tdf102223.odp");
1349 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1350 SdPage* pActualPage = pViewShell->GetActualPage();
1351 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pActualPage->GetObj(2));
1352 CPPUNIT_ASSERT(pTableObject);
1353 SdrView* pView = pViewShell->GetView();
1355 // select contents of cell
1356 ::tools::Rectangle aRect = pTableObject->GetCurrentBoundRect();
1357 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1358 o3tl::toTwips(aRect.Left() + 2, o3tl::Length::mm100), o3tl::toTwips(aRect.Top() + 2, o3tl::Length::mm100),
1359 1, MOUSE_LEFT, 0);
1360 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1361 o3tl::toTwips(aRect.Left() + 2, o3tl::Length::mm100), o3tl::toTwips(aRect.Top() + 2, o3tl::Length::mm100),
1362 1, MOUSE_LEFT, 0);
1363 Scheduler::ProcessEventsToIdle();
1364 pView->SdrBeginTextEdit(pTableObject);
1365 CPPUNIT_ASSERT(pView->GetTextEditObject());
1366 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
1367 rEditView.SetSelection(ESelection(0, 0, 0, 3)); // start para, start char, end para, end char.
1368 CPPUNIT_ASSERT_EQUAL(OUString("Red"), rEditView.GetSelected());
1369 CPPUNIT_ASSERT_EQUAL(
1370 int(1411), static_cast<int>(rEditView.GetAttribs().Get(EE_CHAR_FONTHEIGHT).GetHeight()));
1372 // cut contents of cell
1373 uno::Sequence<beans::PropertyValue> aArgs;
1374 dispatchCommand(mxComponent, ".uno:Cut", aArgs);
1376 pView->SdrEndTextEdit(false);
1377 pView->SdrBeginTextEdit(pTableObject);
1378 CPPUNIT_ASSERT(pView->GetTextEditObject());
1379 EditView& rEditView2 = pView->GetTextEditOutlinerView()->GetEditView();
1380 rEditView2.SetSelection(ESelection(0, 0, 0, 1)); // start para, start char, end para, end char.
1381 CPPUNIT_ASSERT_EQUAL(
1382 int(1411), static_cast<int>(rEditView2.GetAttribs().Get(EE_CHAR_FONTHEIGHT).GetHeight()));
1385 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf118354)
1387 // Load the document.
1388 SdXImpressDocument* pXImpressDocument = createDoc("tdf118354.odp");
1390 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1391 SdPage* pActualPage = pViewShell->GetActualPage();
1393 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pActualPage->GetObjCount());
1395 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pActualPage->GetObj(0));
1396 CPPUNIT_ASSERT(pTableObject);
1398 // Without the fix, it would crash here
1399 ::tools::Rectangle aRect = pTableObject->GetCurrentBoundRect();
1400 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1401 o3tl::toTwips(aRect.Left() + 2, o3tl::Length::mm100), o3tl::toTwips(aRect.Top() + 2, o3tl::Length::mm100),
1402 1, MOUSE_LEFT, 0);
1403 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1404 o3tl::toTwips(aRect.Left() + 2, o3tl::Length::mm100), o3tl::toTwips(aRect.Top() + 2, o3tl::Length::mm100),
1405 1, MOUSE_LEFT, 0);
1406 Scheduler::ProcessEventsToIdle();
1408 SdrView* pView = pViewShell->GetView();
1409 auto pMarkedObj = dynamic_cast<sdr::table::SdrTableObj*>(pView->GetMarkedObjectByIndex(0));
1410 CPPUNIT_ASSERT_EQUAL(pMarkedObj, pTableObject);
1413 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPostKeyEventInvalidation)
1415 // Load a document and begin text edit on the first slide.
1416 SdXImpressDocument* pXImpressDocument = createDoc("2slides.odp");
1417 CPPUNIT_ASSERT_EQUAL(0, pXImpressDocument->getPart());
1418 ViewCallback aView1;
1419 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1420 SdrView* pView = pViewShell->GetView();
1421 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
1422 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB);
1423 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_F2);
1424 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_F2);
1425 Scheduler::ProcessEventsToIdle();
1426 CPPUNIT_ASSERT(pView->GetTextEditObject());
1428 // Create a second view and begin text edit there as well, in parallel.
1429 SfxLokHelper::createView();
1430 pXImpressDocument->initializeForTiledRendering({});
1431 ViewCallback aView2;
1432 pXImpressDocument->setPart(1);
1433 sd::ViewShell* pViewShell2 = pXImpressDocument->GetDocShell()->GetViewShell();
1434 SdrView* pView2 = pViewShell2->GetView();
1435 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
1436 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB);
1437 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_F2);
1438 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_F2);
1439 Scheduler::ProcessEventsToIdle();
1440 CPPUNIT_ASSERT(pView2->GetTextEditObject());
1442 // Now go left with the cursor in the second view and watch for
1443 // invalidations.
1444 aView2.m_bTilesInvalidated = false;
1445 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT);
1446 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT);
1447 Scheduler::ProcessEventsToIdle();
1448 // This failed: moving the cursor caused unexpected invalidation.
1449 CPPUNIT_ASSERT(!aView2.m_bTilesInvalidated);
1453 * tests a cut/paste bug around bullet items in a list
1455 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf103083)
1457 // Load the document.
1458 SdXImpressDocument* pXImpressDocument = createDoc("tdf103083.fodp");
1459 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1460 SdPage* pActualPage = pViewShell->GetActualPage();
1462 SdrObject* pObject1 = pActualPage->GetObj(1);
1463 CPPUNIT_ASSERT_EQUAL(SdrObjKind::OutlineText, pObject1->GetObjIdentifier());
1464 SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
1466 SdrView* pView = pViewShell->GetView();
1468 // select contents of bullet item
1469 ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
1470 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1471 o3tl::toTwips(aRect.Left() + 2, o3tl::Length::mm100), o3tl::toTwips(aRect.Top() + 2, o3tl::Length::mm100),
1472 1, MOUSE_LEFT, 0);
1473 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1474 o3tl::toTwips(aRect.Left() + 2, o3tl::Length::mm100), o3tl::toTwips(aRect.Top() + 2, o3tl::Length::mm100),
1475 1, MOUSE_LEFT, 0);
1476 Scheduler::ProcessEventsToIdle();
1477 pView->SdrBeginTextEdit(pTextObject);
1478 CPPUNIT_ASSERT(pView->GetTextEditObject());
1479 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
1480 rEditView.SetSelection(ESelection(2, 0, 2, 33)); // start para, start char, end para, end char.
1481 CPPUNIT_ASSERT_EQUAL(OUString("They have all the same formatting"), rEditView.GetSelected());
1482 SdrOutliner* pOutliner = pView->GetTextEditOutliner();
1483 CPPUNIT_ASSERT_EQUAL(OUString("No-Logo Content~LT~Gliederung 2"),
1484 pOutliner->GetStyleSheet(2)->GetName());
1485 const EditTextObject& aEdit = pTextObject->GetOutlinerParaObject()->GetTextObject();
1486 const SvxNumBulletItem* pNumFmt = aEdit.GetParaAttribs(2).GetItem(EE_PARA_NUMBULLET);
1487 SvxNumberFormat aNumFmt(pNumFmt->GetNumRule().GetLevel(2));
1489 // cut contents of bullet item
1490 dispatchCommand(mxComponent, ".uno:Cut", uno::Sequence<beans::PropertyValue>());
1492 CPPUNIT_ASSERT(pView->GetTextEditObject());
1493 EditView& rEditView2 = pView->GetTextEditOutlinerView()->GetEditView();
1494 rEditView2.SetSelection(ESelection(2, 0, 2, 10)); // start para, start char, end para, end char.
1495 CPPUNIT_ASSERT_EQUAL(OUString(), rEditView2.GetSelected());
1497 // paste contents of bullet item
1498 dispatchCommand(mxComponent, ".uno:Paste", uno::Sequence<beans::PropertyValue>());
1500 // send an ESC key to trigger the commit of the edit to the main model
1501 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
1502 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
1503 Scheduler::ProcessEventsToIdle();
1505 pView->SdrBeginTextEdit(pTextObject);
1506 CPPUNIT_ASSERT(pView->GetTextEditObject());
1507 pOutliner = pView->GetTextEditOutliner();
1508 EditView& rEditView3 = pView->GetTextEditOutlinerView()->GetEditView();
1509 rEditView3.SetSelection(ESelection(2, 0, 2, 33)); // start para, start char, end para, end char.
1510 CPPUNIT_ASSERT_EQUAL(OUString("They have all the same formatting"), rEditView3.GetSelected());
1511 CPPUNIT_ASSERT_EQUAL(OUString("No-Logo Content~LT~Gliederung 2"),
1512 pOutliner->GetStyleSheet(2)->GetName());
1514 const EditTextObject& aEdit2 = pTextObject->GetOutlinerParaObject()->GetTextObject();
1515 const SvxNumBulletItem* pNumFmt2 = aEdit2.GetParaAttribs(2).GetItem(EE_PARA_NUMBULLET);
1516 SvxNumberFormat aNumFmt2(pNumFmt2->GetNumRule().GetLevel(2));
1518 bool bEqual(aNumFmt2 == aNumFmt);
1519 CPPUNIT_ASSERT_MESSAGE("Bullet properties changed after paste", bEqual);
1523 * tests a clone-formatting bug around table cell attributes
1525 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf104405)
1527 // Load the document.
1528 SdXImpressDocument* pXImpressDocument = createDoc("tdf104405.fodp");
1529 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1530 SdPage* pActualPage = pViewShell->GetActualPage();
1531 SdrObject* pObject = pActualPage->GetObj(2);
1532 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pObject);
1533 CPPUNIT_ASSERT(pTableObject);
1535 // select the middle cell
1536 SdrView* pView = pViewShell->GetView();
1537 pView->MarkObj(pTableObject, pView->GetSdrPageView());
1538 pTableObject->setActiveCell(sdr::table::CellPos(2,1));
1539 pView->SdrBeginTextEdit(pTableObject);
1540 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
1541 rEditView.SetSelection(ESelection(0, 0, 0, 3)); // start para, start char, end para, end char.
1543 // trigger the clone-formatting/paintbrush command to copy formatting contents of cell
1544 uno::Sequence aArgs{ comphelper::makePropertyValue("PersistentCopy", true) };
1545 dispatchCommand(mxComponent, ".uno:FormatPaintbrush", aArgs);
1547 // now click on the table
1548 pView->MarkObj(pTableObject, pView->GetSdrPageView());
1549 pTableObject->setActiveCell(sdr::table::CellPos(0,0));
1550 pView->SdrEndTextEdit(false);
1551 pView->SdrBeginTextEdit(pTableObject);
1552 EditView& rEditView2 = pView->GetTextEditOutlinerView()->GetEditView();
1553 rEditView2.SetSelection(ESelection(0, 0, 0, 3)); // start para, start char, end para, end char.
1554 ::tools::Rectangle aRect = pTableObject->GetCurrentBoundRect();
1555 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1556 o3tl::toTwips(aRect.Left(), o3tl::Length::mm100), o3tl::toTwips(aRect.Top(), o3tl::Length::mm100),
1557 1, MOUSE_LEFT, 0);
1558 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1559 o3tl::toTwips(aRect.Left(), o3tl::Length::mm100), o3tl::toTwips(aRect.Top(), o3tl::Length::mm100),
1560 1, MOUSE_LEFT, 0);
1562 Scheduler::ProcessEventsToIdle();
1564 // check that the first cell has acquired the resulting vertical style
1565 xmlDocUniquePtr pXmlDoc = parseXmlDump();
1566 // the following name has a compiler-dependent part
1567 CPPUNIT_ASSERT_EQUAL(
1568 OUString("2"),
1569 getXPath(
1570 pXmlDoc,
1571 "/SdDrawDocument/SdrModel/maPages/SdPage/SdrPage/SdrObjList/SdrTableObj/SdrTableObjImpl"
1572 "/TableModel/Cell[1]/DefaultProperties/SfxItemSet/SdrTextVertAdjustItem",
1573 "value"));
1576 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf81754)
1578 SdXImpressDocument* pXImpressDocument = createDoc("tdf81754.pptx");
1579 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1580 SdPage* pActualPage = pViewShell->GetActualPage();
1581 SdrObject* pObject = pActualPage->GetObj(1);
1583 SdrTextObj* pTextObj = static_cast<SdrTextObj*>(pObject);
1584 SdrView* pView = pViewShell->GetView();
1585 pView->MarkObj(pTextObj, pView->GetSdrPageView());
1586 SfxStringItem aInputString(SID_ATTR_CHAR, "x");
1587 pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR,
1588 SfxCallMode::SYNCHRON, { &aInputString });
1590 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
1591 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
1592 Scheduler::ProcessEventsToIdle();
1594 // now save, reload, and assert that we did not lose the edit
1595 saveAndReload("Impress Office Open XML");
1597 uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
1598 uno::Reference<drawing::XDrawPage> xPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
1599 uno::UNO_QUERY);
1600 uno::Reference<text::XTextRange> xShape(xPage->getByIndex(1), uno::UNO_QUERY);
1601 CPPUNIT_ASSERT_EQUAL(OUString(u"Somethingxx"), xShape->getString());
1604 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf105502)
1606 // Load the document.
1607 SdXImpressDocument* pXImpressDocument = createDoc("tdf105502.odp");
1608 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1609 sd::Window* pWindow = pViewShell->GetActiveWindow();
1610 CPPUNIT_ASSERT(pWindow);
1611 SdPage* pActualPage = pViewShell->GetActualPage();
1612 SdrObject* pObject = pActualPage->GetObj(0);
1613 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pObject);
1614 CPPUNIT_ASSERT(pTableObject);
1616 // Select the first row.
1617 sd::View* pView = pViewShell->GetView();
1618 pView->MarkObj(pObject, pView->GetSdrPageView());
1619 pView->SdrBeginTextEdit(pObject);
1620 rtl::Reference<sdr::SelectionController> xSelectionController(pView->getSelectionController());
1621 CPPUNIT_ASSERT(xSelectionController.is());
1622 SfxRequest aRequest(*pViewShell->GetViewFrame(), SID_TABLE_SELECT_ROW);
1623 xSelectionController->Execute(aRequest);
1625 // Assert that the A1:B1 selection succeeded.
1626 CPPUNIT_ASSERT(xSelectionController->hasSelectedCells());
1627 sdr::table::CellPos aFirstCell;
1628 sdr::table::CellPos aLastCell;
1629 xSelectionController->getSelectedCells(aFirstCell, aLastCell);
1630 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aFirstCell.mnCol);
1631 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aFirstCell.mnRow);
1632 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aLastCell.mnCol);
1633 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aLastCell.mnRow);
1635 // Grow font size for the selection.
1636 dispatchCommand(mxComponent, ".uno:Grow", {});
1638 // Assert that the selected A1 has now a larger font than the unselected
1639 // A2.
1640 xmlDocUniquePtr pXmlDoc = parseXmlDump();
1641 sal_Int32 nA1Height = getXPath(pXmlDoc, "//Cell[1]/SdrText/OutlinerParaObject/EditTextObject/ContentInfo/SfxItemSet/SvxFontHeightItem[1]", "height").toInt32();
1642 sal_Int32 nA2Height = getXPath(pXmlDoc, "//Cell[3]/SdrText/OutlinerParaObject/EditTextObject/ContentInfo/attribs[1]/SvxFontHeightItem", "height").toInt32();
1643 // This failed when FuText::ChangeFontSize() never did "continue" in the
1644 // text loop, instead of doing so depending on what IsInSelection() returns.
1645 CPPUNIT_ASSERT(nA1Height > nA2Height);
1647 // Check that selection remains the same
1648 CPPUNIT_ASSERT(xSelectionController->hasSelectedCells());
1649 xSelectionController->getSelectedCells(aFirstCell, aLastCell);
1650 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aFirstCell.mnCol);
1651 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aFirstCell.mnRow);
1652 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aLastCell.mnCol);
1653 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aLastCell.mnRow);
1656 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCommentCallbacks)
1658 // Load the document.
1659 // Set the tiled annotations off
1660 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1662 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp", comphelper::InitPropertySequence(
1664 {".uno:Author", uno::Any(OUString("LOK User1"))},
1665 }));
1666 ViewCallback aView1;
1667 int nView1 = SfxLokHelper::getView();
1669 SfxLokHelper::createView();
1670 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
1672 {".uno:Author", uno::Any(OUString("LOK User2"))},
1673 }));
1674 pXImpressDocument->initializeForTiledRendering(aArgs);
1675 ViewCallback aView2;
1676 int nView2 = SfxLokHelper::getView();
1678 SfxLokHelper::setView(nView1);
1680 // Add a new comment
1681 aArgs = comphelper::InitPropertySequence(
1683 {"Text", uno::Any(OUString("Comment"))},
1685 dispatchCommand(mxComponent, ".uno:InsertAnnotation", aArgs);
1687 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
1688 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1689 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1690 int nComment1 = aView1.m_aCommentCallbackResult.get<int>("id");
1691 CPPUNIT_ASSERT_EQUAL(nComment1, aView2.m_aCommentCallbackResult.get<int>("id"));
1692 css::util::DateTime aDateTime;
1693 OUString aDateTimeString = OUString::createFromAscii(aView1.m_aCommentCallbackResult.get<std::string>("dateTime"));
1694 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString, aDateTime));
1695 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1696 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1697 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1698 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1699 CPPUNIT_ASSERT(!aView1.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1700 CPPUNIT_ASSERT(!aView2.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1702 // Reply to a just added comment
1703 SfxLokHelper::setView(nView2);
1704 aArgs = comphelper::InitPropertySequence(
1706 {"Id", uno::Any(OUString::number(nComment1))},
1707 {"Text", uno::Any(OUString("Reply to comment"))},
1709 dispatchCommand(mxComponent, ".uno:ReplyToAnnotation", aArgs);
1711 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
1712 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1713 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1714 CPPUNIT_ASSERT_EQUAL(nComment1, aView1.m_aCommentCallbackResult.get<int>("id"));
1715 CPPUNIT_ASSERT_EQUAL(nComment1, aView2.m_aCommentCallbackResult.get<int>("id"));
1716 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView1.m_aCommentCallbackResult.get<std::string>("author"));
1717 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), aView2.m_aCommentCallbackResult.get<std::string>("author"));
1718 OUString aReplyTextView1 = OUString::createFromAscii(aView1.m_aCommentCallbackResult.get<std::string>("text"));
1719 OUString aReplyTextView2 = OUString::createFromAscii(aView2.m_aCommentCallbackResult.get<std::string>("text"));
1720 CPPUNIT_ASSERT(aReplyTextView1.startsWith("Reply to LOK User1"));
1721 CPPUNIT_ASSERT(aReplyTextView1.endsWith("Reply to comment"));
1722 CPPUNIT_ASSERT(aReplyTextView2.startsWith("Reply to LOK User1"));
1723 CPPUNIT_ASSERT(aReplyTextView2.endsWith("Reply to comment"));
1724 CPPUNIT_ASSERT(!aView1.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1725 CPPUNIT_ASSERT(!aView2.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1727 // Edit this annotation now
1728 aArgs = comphelper::InitPropertySequence(
1730 {"Id", uno::Any(OUString::number(nComment1))},
1731 {"Text", uno::Any(OUString("Edited comment"))},
1733 dispatchCommand(mxComponent, ".uno:EditAnnotation", aArgs);
1735 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
1736 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1737 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1738 CPPUNIT_ASSERT_EQUAL(nComment1, aView1.m_aCommentCallbackResult.get<int>("id"));
1739 CPPUNIT_ASSERT_EQUAL(nComment1, aView2.m_aCommentCallbackResult.get<int>("id"));
1740 CPPUNIT_ASSERT(!aView1.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1741 CPPUNIT_ASSERT(!aView2.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1742 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1743 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
1745 // Delete the comment
1746 aArgs = comphelper::InitPropertySequence(
1748 {"Id", uno::Any(OUString::number(nComment1))},
1750 dispatchCommand(mxComponent, ".uno:DeleteAnnotation", aArgs);
1752 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
1753 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1754 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
1755 CPPUNIT_ASSERT_EQUAL(nComment1, aView1.m_aCommentCallbackResult.get<int>("id"));
1756 CPPUNIT_ASSERT_EQUAL(nComment1, aView2.m_aCommentCallbackResult.get<int>("id"));
1758 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1761 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCommentChangeImpress)
1763 uno::Sequence<beans::PropertyValue> aArgs;
1765 // Load the document.
1766 // Set the tiled annotations off
1767 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1769 createDoc("dummy.odp", comphelper::InitPropertySequence(
1771 {".uno:Author", uno::Any(OUString("LOK User1"))},
1772 }));
1774 ViewCallback aView1;
1776 // Add a new comment
1777 aArgs = comphelper::InitPropertySequence(
1779 {"Text", uno::Any(OUString("Comment"))},
1781 dispatchCommand(mxComponent, ".uno:InsertAnnotation", aArgs);
1783 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1785 int nComment1 = aView1.m_aCommentCallbackResult.get<int>("id");
1787 CPPUNIT_ASSERT(!aView1.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1788 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1789 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 0, 0"), aView1.m_aCommentCallbackResult.get<std::string>("rectangle"));
1791 // Edit this annotation now
1792 aArgs = comphelper::InitPropertySequence(
1794 {"Id", uno::Any(OUString::number(nComment1))},
1795 {"PositionX", uno::Any(sal_Int32(10))},
1796 {"PositionY", uno::Any(sal_Int32(20))}
1798 dispatchCommand(mxComponent, ".uno:EditAnnotation", aArgs);
1800 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1801 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1802 CPPUNIT_ASSERT_EQUAL(std::string("10, 20, 0, 0"), aView1.m_aCommentCallbackResult.get<std::string>("rectangle"));
1804 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1807 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCommentChangeDraw)
1809 uno::Sequence<beans::PropertyValue> aArgs;
1811 // Load the document.
1812 // Set the tiled annotations off
1813 comphelper::LibreOfficeKit::setTiledAnnotations(false);
1815 createDoc("dummy.odg", comphelper::InitPropertySequence(
1817 {".uno:Author", uno::Any(OUString("LOK User1"))},
1818 }));
1820 ViewCallback aView1;
1822 // Add a new comment
1823 aArgs = comphelper::InitPropertySequence(
1825 {"Text", uno::Any(OUString("Comment"))},
1827 dispatchCommand(mxComponent, ".uno:InsertAnnotation", aArgs);
1829 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1831 int nComment1 = aView1.m_aCommentCallbackResult.get<int>("id");
1833 CPPUNIT_ASSERT(!aView1.m_aCommentCallbackResult.get<std::string>("parthash").empty());
1834 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1835 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 0, 0"), aView1.m_aCommentCallbackResult.get<std::string>("rectangle"));
1837 // Edit this annotation now
1838 aArgs = comphelper::InitPropertySequence(
1840 {"Id", uno::Any(OUString::number(nComment1))},
1841 {"PositionX", uno::Any(sal_Int32(10))},
1842 {"PositionY", uno::Any(sal_Int32(20))}
1844 dispatchCommand(mxComponent, ".uno:EditAnnotation", aArgs);
1846 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
1847 CPPUNIT_ASSERT_EQUAL(std::string("Comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
1848 CPPUNIT_ASSERT_EQUAL(std::string("10, 20, 0, 0"), aView1.m_aCommentCallbackResult.get<std::string>("rectangle"));
1850 comphelper::LibreOfficeKit::setTiledAnnotations(true);
1853 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testMultiViewInsertDeletePage)
1855 // Load the document.
1856 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1857 ViewCallback aView1;
1858 int nView1 = SfxLokHelper::getView();
1859 uno::Sequence<beans::PropertyValue> aArgs;
1860 SdDrawDocument* pDoc = pXImpressDocument->GetDocShell()->GetDoc();
1862 // Create second view
1863 SfxLokHelper::createView();
1864 pXImpressDocument->initializeForTiledRendering(aArgs);
1865 ViewCallback aView2;
1866 int nView2 = SfxLokHelper::getView();
1868 // the document has 8 slides
1869 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(8), pDoc->GetSdPageCount(PageKind::Standard));
1871 // Switch to 5th page in 2nd view
1872 pXImpressDocument->setPart(4);
1874 // Insert slide in 1st view
1875 SfxLokHelper::setView(nView1);
1876 dispatchCommand(mxComponent, ".uno:InsertPage", aArgs);
1878 // See if the current slide number changed in 2nd view too
1879 SfxLokHelper::setView(nView2);
1880 CPPUNIT_ASSERT_EQUAL(5, pXImpressDocument->getPart());
1882 // Delete the page in 1st view now
1883 SfxLokHelper::setView(nView1);
1884 dispatchCommand(mxComponent, ".uno:DeletePage", aArgs);
1886 // See if current slide number changed in 2nd view too
1887 SfxLokHelper::setView(nView2);
1888 CPPUNIT_ASSERT_EQUAL(4, pXImpressDocument->getPart());
1891 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testMultiViewInsertDeletePage2)
1893 // Load the document.
1894 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1895 ViewCallback aView1;
1896 int nView1 = SfxLokHelper::getView();
1897 uno::Sequence<beans::PropertyValue> aArgs;
1898 SdDrawDocument* pDoc = pXImpressDocument->GetDocShell()->GetDoc();
1900 // Create second view
1901 SfxLokHelper::createView();
1902 pXImpressDocument->initializeForTiledRendering(aArgs);
1903 ViewCallback aView2;
1904 int nView2 = SfxLokHelper::getView();
1906 // the document has 8 slides
1907 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(8), pDoc->GetSdPageCount(PageKind::Standard));
1909 // Switch to 5th page in 2nd view
1910 pXImpressDocument->setPart(4);
1912 // Begin text edit on the only object on the slide.
1913 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
1914 SdPage* pActualPage = pViewShell->GetActualPage();
1915 SdrObject* pObject1 = pActualPage->GetObj(0);
1916 CPPUNIT_ASSERT(pObject1 != nullptr);
1917 CPPUNIT_ASSERT_EQUAL(SdrObjKind::TitleText, pObject1->GetObjIdentifier());
1918 SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
1920 // Double-click outside the text to enter edit mode.
1921 const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
1922 const auto cornerX = o3tl::toTwips(aRect.Left() + (aRect.getOpenWidth() / 4), o3tl::Length::mm100);
1923 const auto cornerY = o3tl::toTwips(aRect.Top() + (aRect.getOpenHeight() / 4), o3tl::Length::mm100);
1924 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
1925 cornerX, cornerY,
1926 2, MOUSE_LEFT, 0);
1927 pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
1928 cornerX, cornerY,
1929 2, MOUSE_LEFT, 0);
1930 Scheduler::ProcessEventsToIdle();
1932 // We must be in text editing mode and have cursor visible.
1933 CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
1935 // Insert slide in 1st view
1936 SfxLokHelper::setView(nView1);
1937 dispatchCommand(mxComponent, ".uno:InsertPage", aArgs);
1939 // See if the current slide number changed in 2nd view too
1940 SfxLokHelper::setView(nView2);
1941 CPPUNIT_ASSERT_EQUAL(5, pXImpressDocument->getPart());
1943 // Delete the page in 1st view now
1944 SfxLokHelper::setView(nView1);
1945 dispatchCommand(mxComponent, ".uno:DeletePage", aArgs);
1947 // See if current slide number changed in 2nd view too
1948 SfxLokHelper::setView(nView2);
1949 CPPUNIT_ASSERT_EQUAL(4, pXImpressDocument->getPart());
1951 // We must be still in text editing mode and have cursor visible.
1952 CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
1955 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testDisableUndoRepair)
1957 // Load the document.
1958 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
1960 // Create View 1
1961 SfxViewShell* pView1 = SfxViewShell::Current();
1962 sd::ViewShell* pViewShell1 = pXImpressDocument->GetDocShell()->GetViewShell();
1963 int nView1 = SfxLokHelper::getView();
1965 // Create View 2
1966 SfxLokHelper::createView();
1967 SfxViewShell* pView2 = SfxViewShell::Current();
1968 sd::ViewShell* pViewShell2 = pXImpressDocument->GetDocShell()->GetViewShell();
1969 int nView2 = SfxLokHelper::getView();
1971 // Check UNDO is disabled
1973 std::unique_ptr<SfxPoolItem> pItem1;
1974 std::unique_ptr<SfxPoolItem> pItem2;
1975 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, pView1->GetViewFrame().GetBindings().QueryState(SID_UNDO, pItem1));
1976 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, pView2->GetViewFrame().GetBindings().QueryState(SID_UNDO, pItem2));
1979 // Insert a character in the first view.
1980 SfxLokHelper::setView(nView1);
1981 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
1982 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
1983 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'h', 0);
1984 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'h', 0);
1985 Scheduler::ProcessEventsToIdle();
1986 CPPUNIT_ASSERT(pViewShell1->GetView()->IsTextEdit());
1987 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
1988 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
1989 Scheduler::ProcessEventsToIdle();
1990 CPPUNIT_ASSERT(!pViewShell1->GetView()->IsTextEdit());
1992 // Check
1994 std::unique_ptr<SfxPoolItem> xItem1;
1995 pView1->GetViewFrame().GetBindings().QueryState(SID_UNDO, xItem1);
1996 const auto* pUInt32Item1 = dynamic_cast<const SfxUInt32Item*>(xItem1.get());
1997 CPPUNIT_ASSERT(!pUInt32Item1);
1999 std::unique_ptr<SfxPoolItem> xItem2;
2000 pView2->GetViewFrame().GetBindings().QueryState(SID_UNDO, xItem2);
2001 const auto* pUInt32Item2 = dynamic_cast<const SfxUInt32Item*>(xItem2.get());
2002 CPPUNIT_ASSERT(pUInt32Item2);
2003 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item2->GetValue());
2006 // Insert a character in the second view.
2007 SfxLokHelper::setView(nView2);
2008 pXImpressDocument->setPart(1);
2009 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
2010 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
2011 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
2012 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
2013 Scheduler::ProcessEventsToIdle();
2014 CPPUNIT_ASSERT(pViewShell2->GetView()->IsTextEdit());
2015 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
2016 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
2017 Scheduler::ProcessEventsToIdle();
2018 CPPUNIT_ASSERT(!pViewShell2->GetView()->IsTextEdit());
2020 // Check
2022 std::unique_ptr<SfxPoolItem> xItem1;
2023 pView1->GetViewFrame().GetBindings().QueryState(SID_UNDO, xItem1);
2024 const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(xItem1.get());
2025 CPPUNIT_ASSERT(pUInt32Item);
2026 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
2028 std::unique_ptr<SfxPoolItem> xItem2;
2029 pView2->GetViewFrame().GetBindings().QueryState(SID_UNDO, xItem2);
2030 CPPUNIT_ASSERT(!dynamic_cast< const SfxUInt32Item* >(xItem2.get()));
2034 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testDocumentRepair)
2036 // Create two views.
2037 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
2038 CPPUNIT_ASSERT(pXImpressDocument);
2040 // view #1
2041 SfxViewShell* pView1 = SfxViewShell::Current();
2043 // view #2
2044 SfxLokHelper::createView();
2045 SfxViewShell* pView2 = SfxViewShell::Current();
2046 int nView2 = SfxLokHelper::getView();
2047 sd::ViewShell* pViewShell2 = pXImpressDocument->GetDocShell()->GetViewShell();
2049 CPPUNIT_ASSERT(pView1 != pView2);
2051 std::unique_ptr<SfxBoolItem> pItem1;
2052 pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
2053 CPPUNIT_ASSERT(pItem1);
2054 CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
2056 std::unique_ptr<SfxBoolItem> pItem2;
2057 pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
2058 CPPUNIT_ASSERT(pItem2);
2059 CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
2062 // Insert a character in the second view.
2063 SfxLokHelper::setView(nView2);
2064 pXImpressDocument->setPart(1);
2065 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
2066 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
2067 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', 0);
2068 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', 0);
2069 Scheduler::ProcessEventsToIdle();
2070 CPPUNIT_ASSERT(pViewShell2->GetView()->IsTextEdit());
2071 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
2072 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
2073 Scheduler::ProcessEventsToIdle();
2074 CPPUNIT_ASSERT(!pViewShell2->GetView()->IsTextEdit());
2077 std::unique_ptr<SfxBoolItem> pItem1;
2078 pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
2079 CPPUNIT_ASSERT(pItem1);
2080 CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
2082 std::unique_ptr<SfxBoolItem> pItem2;
2083 pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
2084 CPPUNIT_ASSERT(pItem2);
2085 CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
2089 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testLanguageStatus)
2091 // Load the document.
2092 createDoc("dummy.odp");
2093 SfxViewShell* pView1 = SfxViewShell::Current();
2094 SfxLokHelper::createView();
2095 SfxViewShell* pView2 = SfxViewShell::Current();
2097 std::unique_ptr<SfxPoolItem> xItem1;
2098 std::unique_ptr<SfxPoolItem> xItem2;
2099 pView1->GetViewFrame().GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem1);
2100 pView2->GetViewFrame().GetBindings().QueryState(SID_LANGUAGE_STATUS, xItem2);
2101 auto pStringItem = dynamic_cast<const SfxStringItem*>(xItem1.get());
2102 CPPUNIT_ASSERT(pStringItem);
2104 CPPUNIT_ASSERT_EQUAL(OUString("English (USA);en-US"), pStringItem->GetValue());
2106 CPPUNIT_ASSERT(dynamic_cast< const SfxStringItem* >(xItem2.get()));
2110 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testLanguageAllText)
2112 // Load the document, which has a single shape, with Hungarian text.
2113 createDoc("language-all-text.odp");
2115 // Set the language to English for all text.
2116 uno::Sequence<beans::PropertyValue> aArgs = comphelper::InitPropertySequence({
2117 { "Language", uno::Any(OUString("Default_English (USA)")) },
2119 dispatchCommand(mxComponent, ".uno:LanguageStatus", aArgs);
2121 // Assert that the shape text language was changed.
2122 uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
2123 uno::Reference<drawing::XDrawPage> xPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
2124 uno::UNO_QUERY);
2125 uno::Reference<text::XTextRange> xShape(xPage->getByIndex(0), uno::UNO_QUERY);
2126 uno::Reference<beans::XPropertySet> xRun(xShape, uno::UNO_QUERY_THROW);
2127 lang::Locale aLocale;
2128 xRun->getPropertyValue("CharLocale") >>= aLocale;
2129 // Without the accompanying fix in place, this test would have failed with 'Expected: en;
2130 // Actual: hu', as the shape text language was not set.
2131 CPPUNIT_ASSERT_EQUAL(OUString("en"), aLocale.Language);
2134 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testDefaultView)
2136 // Load the document with notes view.
2137 SdXImpressDocument* pXImpressDocument = createDoc("notes-view.odp");
2138 sd::ViewShell* pView = pXImpressDocument->GetDocShell()->GetViewShell();
2140 std::unique_ptr<SfxBoolItem> pImpressView;
2141 std::unique_ptr<SfxBoolItem> pNotesView;
2142 pView->GetViewFrame()->GetBindings().QueryState(SID_NORMAL_MULTI_PANE_GUI, pImpressView);
2143 pView->GetViewFrame()->GetBindings().QueryState(SID_NOTES_MODE, pNotesView);
2144 CPPUNIT_ASSERT(pImpressView);
2145 CPPUNIT_ASSERT(pNotesView);
2146 CPPUNIT_ASSERT_EQUAL(true, pImpressView->GetValue());
2147 CPPUNIT_ASSERT_EQUAL(false, pNotesView->GetValue());
2151 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testIMESupport)
2153 // Load the document with notes view.
2154 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
2155 VclPtr<vcl::Window> pDocWindow = pXImpressDocument->getDocWindow();
2156 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
2157 SdrObject* pObject = pViewShell->GetActualPage()->GetObj(0);
2158 SdrTextObj* pTextObj = static_cast<SdrTextObj*>(pObject);
2159 SdrView* pView = pViewShell->GetView();
2160 pView->MarkObj(pTextObj, pView->GetSdrPageView());
2161 SfxStringItem aInputString(SID_ATTR_CHAR, "x");
2162 pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR,
2163 SfxCallMode::SYNCHRON, { &aInputString });
2165 // sequence of chinese IME compositions when 'nihao' is typed in an IME
2166 const std::vector<OString> aUtf8Inputs{ "年", "你", "你好", "你哈", "你好", "你好" };
2167 std::vector<OUString> aInputs;
2168 std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
2169 std::back_inserter(aInputs), [](OString aInput) {
2170 return OUString::fromUtf8(aInput);
2172 for (const auto& aInput: aInputs)
2174 pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
2176 pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
2178 // the cursor should be at position 3rd
2179 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
2180 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), rEditView.GetSelection().nStartPos);
2182 ESelection aWordSelection(0, 0, 0, 3); // start para, start char, end para, end char.
2183 rEditView.SetSelection(aWordSelection);
2184 // content contains only the last IME composition, not all
2185 CPPUNIT_ASSERT_EQUAL(OUString("x" + aInputs[aInputs.size() - 1]), rEditView.GetSelected());
2188 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf115783)
2190 // Load the document.
2191 SdXImpressDocument* pXImpressDocument = createDoc("tdf115783.fodp");
2192 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
2193 SdPage* pActualPage = pViewShell->GetActualPage();
2194 SdrObject* pObject = pActualPage->GetObj(0);
2195 auto pTableObject = dynamic_cast<sdr::table::SdrTableObj*>(pObject);
2196 CPPUNIT_ASSERT(pTableObject);
2197 SdrView* pView = pViewShell->GetView();
2198 pView->MarkObj(pTableObject, pView->GetSdrPageView());
2200 // Create a cell selection and set font height.
2201 // Go to the end of the B1 cell.
2202 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT);
2203 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT);
2204 // Create a B1->C1 cell selection.
2205 const int nShiftRight = KEY_SHIFT + KEY_RIGHT;
2206 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, nShiftRight);
2207 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, nShiftRight);
2208 uno::Sequence<beans::PropertyValue> aArgs = comphelper::InitPropertySequence({
2209 { "FontHeight.Height", uno::Any(static_cast<float>(12)) },
2211 dispatchCommand(mxComponent, ".uno:FontHeight", aArgs);
2213 // Create a text selection on the B1 cell.
2214 pTableObject->setActiveCell(sdr::table::CellPos(1, 0));
2215 pView->SdrBeginTextEdit(pTableObject);
2216 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
2217 // Start para, start char, end para, end char.
2218 rEditView.SetSelection(ESelection(0, 0, 0, 5));
2219 CPPUNIT_ASSERT_EQUAL(OUString("hello"), rEditView.GetSelected());
2221 // Copy selection, paste at the start of the cell.
2222 aArgs = {};
2223 dispatchCommand(mxComponent, ".uno:Copy", aArgs);
2224 rEditView.SetSelection(ESelection(0, 0, 0, 0));
2225 aArgs = {};
2226 dispatchCommand(mxComponent, ".uno:Paste", aArgs);
2227 pView->SdrEndTextEdit();
2229 // And now verify that the cell has the correct font size.
2230 uno::Reference<table::XCellRange> xTable = pTableObject->getTable();
2231 CPPUNIT_ASSERT(xTable.is());
2232 uno::Reference<text::XTextRange> xCell(xTable->getCellByPosition(1, 0), uno::UNO_QUERY);
2233 CPPUNIT_ASSERT(xCell.is());
2234 uno::Reference<container::XEnumerationAccess> xText(xCell->getText(), uno::UNO_QUERY);
2235 CPPUNIT_ASSERT(xText.is());
2236 uno::Reference<container::XEnumerationAccess> xParagraph(
2237 xText->createEnumeration()->nextElement(), uno::UNO_QUERY);
2238 CPPUNIT_ASSERT(xParagraph.is());
2239 uno::Reference<text::XTextRange> xPortion(xParagraph->createEnumeration()->nextElement(),
2240 uno::UNO_QUERY);
2241 CPPUNIT_ASSERT(xPortion.is());
2242 // This failed, it was only "hello" as the paragraph had 2 portions: a
2243 // "hello" with 12pt size and a "hello" with 18pt.
2244 CPPUNIT_ASSERT_EQUAL(OUString("hellohello"), xPortion->getString());
2245 uno::Reference<beans::XPropertySet> xPropertySet(xPortion, uno::UNO_QUERY);
2246 int nHeight = xPropertySet->getPropertyValue("CharHeight").get<float>();
2247 // Make sure that the single font size for the cell is the expected one.
2248 CPPUNIT_ASSERT_EQUAL(12, nHeight);
2251 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPasteTextOnSlide)
2253 // Load the document.
2254 SdXImpressDocument* pXImpressDocument = createDoc("paste_text_onslide.odp");
2255 CPPUNIT_ASSERT(pXImpressDocument);
2257 // select second text object
2258 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
2259 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
2260 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
2261 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
2262 Scheduler::ProcessEventsToIdle();
2264 // step into text editing
2265 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '1', 0);
2266 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, '1', 0);
2267 Scheduler::ProcessEventsToIdle();
2269 // select full text
2270 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2271 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2272 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2273 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2274 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2275 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2276 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2277 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2278 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2279 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2280 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2281 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2282 Scheduler::ProcessEventsToIdle();
2284 // Copy some text
2285 dispatchCommand(mxComponent, ".uno:Copy", uno::Sequence<beans::PropertyValue>());
2287 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
2288 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
2289 Scheduler::ProcessEventsToIdle();
2291 // Paste onto the slide
2292 dispatchCommand(mxComponent, ".uno:Paste", uno::Sequence<beans::PropertyValue>());
2294 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
2295 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
2296 Scheduler::ProcessEventsToIdle();
2298 // Check the position of the newly added text shape, created for pasted text
2299 SdPage* pActualPage = pXImpressDocument->GetDocShell()->GetViewShell()->GetActualPage();
2300 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pActualPage->GetObjCount());
2301 SdrObject* pObject = pActualPage->GetObj(2);
2302 CPPUNIT_ASSERT(pObject);
2303 SdrTextObj* pTextObj = DynCastSdrTextObj(pObject);
2304 CPPUNIT_ASSERT(pTextObj);
2305 CPPUNIT_ASSERT_EQUAL(SdrObjKind::Text, pTextObj->GetObjIdentifier());
2306 const Point aPos = pTextObj->GetLastBoundRect().TopLeft();
2307 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(0), aPos.getX());
2308 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(0), aPos.getY());
2311 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf115873)
2313 // Initialize the navigator.
2314 SdXImpressDocument* pXImpressDocument = createDoc("tdf115873.fodp");
2315 SfxViewShell* pViewShell = SfxViewShell::Current();
2316 CPPUNIT_ASSERT(pViewShell);
2317 SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
2318 auto xNavigator = std::make_unique<SdNavigatorWin>(nullptr, &rBindings, nullptr);
2319 xNavigator->InitTreeLB(pXImpressDocument->GetDoc());
2320 SdPageObjsTLV& rObjects = xNavigator->GetObjects();
2321 rObjects.SelectEntry(u"Slide 1");
2322 rObjects.Select();
2323 sd::ViewShell* pSdViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
2324 SdrView* pSdrView = pSdViewShell->GetView();
2325 pSdrView->UnmarkAllObj(pSdrView->GetSdrPageView());
2327 // Make sure that no shapes are selected.
2328 const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
2329 Scheduler::ProcessEventsToIdle();
2330 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rMarkList.GetMarkCount());
2332 // Single-click with the mouse.
2333 MouseEvent aMouseEvent(Point(0, 0), /*nClicks=*/1, MouseEventModifiers::NONE, MOUSE_LEFT);
2334 rObjects.MousePressHdl(aMouseEvent);
2335 rObjects.SelectEntry(u"Rectangle");
2336 rObjects.Select();
2337 rObjects.MouseReleaseHdl(aMouseEvent);
2338 Scheduler::ProcessEventsToIdle();
2339 // This failed, single-click did not result in a shape selection (only
2340 // double-click did).
2341 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rMarkList.GetMarkCount());
2344 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testTdf115873Group)
2346 // Initialize the navigator.
2347 SdXImpressDocument* pXImpressDocument = createDoc("tdf115873-group.fodp");
2348 SfxViewShell* pViewShell = SfxViewShell::Current();
2349 CPPUNIT_ASSERT(pViewShell);
2350 SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
2351 auto xNavigator = std::make_unique<SdNavigatorWin>(nullptr, &rBindings, nullptr);
2352 xNavigator->InitTreeLB(pXImpressDocument->GetDoc());
2353 SdPageObjsTLV& rObjects = xNavigator->GetObjects();
2354 // This failed, Fill() and IsEqualToDoc() were out of sync for group
2355 // shapes.
2356 CPPUNIT_ASSERT(rObjects.IsEqualToDoc(pXImpressDocument->GetDoc()));
2359 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testCutSelectionChange)
2361 // Load the document.
2362 SdXImpressDocument* pXImpressDocument = createDoc("cut_selection_change.odp");
2363 CPPUNIT_ASSERT(pXImpressDocument);
2365 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
2366 setupLibreOfficeKitViewCallback(pViewShell->GetViewShellBase());
2367 Scheduler::ProcessEventsToIdle();
2369 // Select first text object
2370 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
2371 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
2372 Scheduler::ProcessEventsToIdle();
2374 // step into text editing
2375 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '1', 0);
2376 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, '1', 0);
2377 Scheduler::ProcessEventsToIdle();
2379 // select some text
2380 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2381 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2382 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT | KEY_SHIFT);
2383 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_LEFT | KEY_SHIFT);
2384 Scheduler::ProcessEventsToIdle();
2386 // Check that we have a selection before cutting
2387 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), m_aSelection.size());
2389 // Cut the selected text
2390 dispatchCommand(mxComponent, ".uno:Cut", uno::Sequence<beans::PropertyValue>());
2392 // Selection is removed
2393 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(0), m_aSelection.size());
2396 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testGetViewRenderState)
2398 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
2399 int nFirstViewId = SfxLokHelper::getView();
2400 ViewCallback aView1;
2401 CPPUNIT_ASSERT_EQUAL(OString(";Default"), pXImpressDocument->getViewRenderState());
2402 // Create a second view
2403 SfxLokHelper::createView();
2404 ViewCallback aView2;
2405 CPPUNIT_ASSERT_EQUAL(OString(";Default"), pXImpressDocument->getViewRenderState());
2406 // Set to dark scheme
2408 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
2410 { "NewTheme", uno::Any(OUString("Dark")) },
2413 dispatchCommand(mxComponent, ".uno:ChangeTheme", aPropertyValues);
2415 CPPUNIT_ASSERT_EQUAL(OString(";Dark"), pXImpressDocument->getViewRenderState());
2416 // Switch back to the first view, and check that the options string is the same
2417 SfxLokHelper::setView(nFirstViewId);
2418 CPPUNIT_ASSERT_EQUAL(OString(";Default"), pXImpressDocument->getViewRenderState());
2421 // Helper function to get a tile to a bitmap and check the pixel color
2422 static void assertTilePixelColor(SdXImpressDocument* pXImpressDocument, int nPixelX, int nPixelY, Color aColor)
2424 size_t nCanvasSize = 1024;
2425 size_t nTileSize = 256;
2426 std::vector<unsigned char> aPixmap(nCanvasSize * nCanvasSize * 4, 0);
2427 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
2428 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2429 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasSize, nCanvasSize),
2430 Fraction(1.0), Point(), aPixmap.data());
2431 pXImpressDocument->paintTile(*pDevice, nCanvasSize, nCanvasSize, 0, 0, 15360, 7680);
2432 pDevice->EnableMapMode(false);
2433 Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
2434 Bitmap::ScopedReadAccess pAccess(aBitmap);
2435 Color aActualColor(pAccess->GetPixel(nPixelX, nPixelY));
2436 CPPUNIT_ASSERT_EQUAL(aColor, aActualColor);
2439 // Test that changing the theme in one view doesn't change it in the other view
2440 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testThemeViewSeparation)
2442 Color aDarkColor(0x1c, 0x1c, 0x1c);
2443 // Add a minimal dark scheme
2445 svtools::EditableColorConfig aColorConfig;
2446 svtools::ColorConfigValue aValue;
2447 aValue.bIsVisible = true;
2448 aValue.nColor = aDarkColor;
2449 aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
2450 aColorConfig.AddScheme(u"Dark");
2452 // Add a minimal light scheme
2454 svtools::EditableColorConfig aColorConfig;
2455 svtools::ColorConfigValue aValue;
2456 aValue.bIsVisible = true;
2457 aValue.nColor = COL_WHITE;
2458 aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
2459 aColorConfig.AddScheme(u"Light");
2461 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
2462 int nFirstViewId = SfxLokHelper::getView();
2463 ViewCallback aView1;
2464 // Switch first view to light scheme
2466 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
2468 { "NewTheme", uno::Any(OUString("Light")) },
2471 dispatchCommand(mxComponent, ".uno:ChangeTheme", aPropertyValues);
2473 // First view is at light scheme
2474 assertTilePixelColor(pXImpressDocument, 255, 255, COL_WHITE);
2475 // Create second view
2476 SfxLokHelper::createView();
2477 int nSecondViewId = SfxLokHelper::getView();
2478 ViewCallback aView2;
2479 // Set second view to dark scheme
2481 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
2483 { "NewTheme", uno::Any(OUString("Dark")) },
2486 dispatchCommand(mxComponent, ".uno:ChangeTheme", aPropertyValues);
2488 assertTilePixelColor(pXImpressDocument, 255, 255, aDarkColor);
2489 // First view still in light scheme
2490 SfxLokHelper::setView(nFirstViewId);
2491 assertTilePixelColor(pXImpressDocument, 255, 255, COL_WHITE);
2492 // Second view still in dark scheme
2493 SfxLokHelper::setView(nSecondViewId);
2494 assertTilePixelColor(pXImpressDocument, 255, 255, Color(0x1c, 0x1c, 0x1c));
2495 // Switch second view back to light scheme
2497 uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
2499 { "NewTheme", uno::Any(OUString("Light")) },
2502 dispatchCommand(mxComponent, ".uno:ChangeTheme", aPropertyValues);
2504 // Now in light scheme
2505 assertTilePixelColor(pXImpressDocument, 255, 255, COL_WHITE);
2508 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testRegenerateDiagram)
2510 // Load the document.
2511 SdXImpressDocument* pXImpressDocument = createDoc("regenerate-diagram.pptx");
2512 CPPUNIT_ASSERT(pXImpressDocument);
2514 SdPage* pActualPage = pXImpressDocument->GetDocShell()->GetViewShell()->GetActualPage();
2515 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), pActualPage->GetObj(0)->GetSubList()->GetObjCount());
2517 // For new Diagram functionality entering group using UI is not allowed as long
2518 // as the group shape is a diagram. Do the same as before done by triggering UI
2519 // events directly in the model
2520 // Remove and free top-left entry (Box showing "A")
2521 pActualPage->GetObj(0)->GetSubList()->RemoveObject(1);
2523 // select diagram
2524 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
2525 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
2526 Scheduler::ProcessEventsToIdle();
2528 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pActualPage->GetObj(0)->GetSubList()->GetObjCount());
2530 // regenerate diagram
2531 dispatchCommand(mxComponent, ".uno:RegenerateDiagram", uno::Sequence<beans::PropertyValue>());
2533 // diagram content (child shape count) should be the same as in the beginning
2534 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), pActualPage->GetObj(0)->GetSubList()->GetObjCount());
2537 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testInsertDeletePageInvalidation)
2539 // Load the document.
2540 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
2541 ViewCallback aView1;
2542 CPPUNIT_ASSERT_EQUAL(8, pXImpressDocument->getParts());
2544 // Insert slide
2545 aView1.m_bTilesInvalidated = false;
2546 aView1.m_aInvalidations.clear();
2547 dispatchCommand(mxComponent, ".uno:InsertPage", uno::Sequence<beans::PropertyValue>());
2548 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
2549 CPPUNIT_ASSERT_EQUAL(9, pXImpressDocument->getParts());
2550 CPPUNIT_ASSERT_EQUAL(size_t(9), aView1.m_aInvalidations.size());
2552 // Delete slide
2553 aView1.m_bTilesInvalidated = false;
2554 aView1.m_aInvalidations.clear();
2555 dispatchCommand(mxComponent, ".uno:DeletePage", uno::Sequence<beans::PropertyValue>());
2556 CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
2557 CPPUNIT_ASSERT_EQUAL(8, pXImpressDocument->getParts());
2558 CPPUNIT_ASSERT_EQUAL(size_t(8), aView1.m_aInvalidations.size());
2561 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSpellOnlineRenderParameter)
2563 // Load the document.
2564 SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
2565 bool bSet = pXImpressDocument->GetDoc()->GetOnlineSpell();
2567 uno::Sequence<beans::PropertyValue> aPropertyValues =
2569 comphelper::InitPropertySequence({ { ".uno:SpellOnline", uno::Any(!bSet) } }),
2571 pXImpressDocument->initializeForTiledRendering(aPropertyValues);
2572 CPPUNIT_ASSERT_EQUAL(!bSet, pXImpressDocument->GetDoc()->GetOnlineSpell());
2575 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSlideDuplicateUndo)
2577 // Create two views.
2578 SdXImpressDocument* pXImpressDocument = createDoc("duplicate-undo.odp");
2579 int nView0 = SfxLokHelper::getView();
2580 SfxLokHelper::createView();
2581 pXImpressDocument->initializeForTiledRendering({});
2582 int nView1 = SfxLokHelper::getView();
2583 SfxLokHelper::setView(nView0);
2585 // Switch to the 3rd slide on view 0, and start text editing.
2587 pXImpressDocument->setPart(2);
2588 sd::ViewShell* pViewShell0 = pXImpressDocument->GetDocShell()->GetViewShell();
2589 SdrView* pView = pViewShell0->GetView();
2590 SdPage* pActualPage = pViewShell0->GetActualPage();
2591 SdrObject* pObject = pActualPage->GetObj(1);
2592 SdrTextObj* pTextObj = static_cast<SdrTextObj*>(pObject);
2593 pView->MarkObj(pTextObj, pView->GetSdrPageView());
2594 SfxStringItem aInputString(SID_ATTR_CHAR, "x");
2595 pViewShell0->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR,
2596 SfxCallMode::SYNCHRON, { &aInputString });
2597 CPPUNIT_ASSERT(pView->IsTextEdit());
2598 CPPUNIT_ASSERT(pView->GetTextEditPageView());
2601 // Duplicate the first slide on view 1 and undo it.
2602 SfxLokHelper::setView(nView1);
2603 dispatchCommand(mxComponent, ".uno:DuplicatePage", {});
2604 pXImpressDocument->setPart(0, /*bAllowChangeFocus=*/false);
2605 pXImpressDocument->setPart(1, /*bAllowChangeFocus=*/false);
2606 SfxLokHelper::setView(nView0);
2607 pXImpressDocument->setPart(0, /*bAllowChangeFocus=*/false);
2608 pXImpressDocument->setPart(3, /*bAllowChangeFocus=*/false);
2609 SfxLokHelper::setView(nView1);
2610 pXImpressDocument->getUndoManager()->undo();
2611 // Without the accompanying fix in place, this would have tried to access the outdated page view
2612 // pointer, potentially leading to a crash.
2613 pXImpressDocument->setPart(2, /*bAllowChangeFocus=*/false);
2615 // Make sure that view 0 now doesn't have an outdated page view pointer.
2616 SfxLokHelper::setView(nView0);
2617 sd::ViewShell* pViewShell0 = pXImpressDocument->GetDocShell()->GetViewShell();
2618 SdrView* pView0 = pViewShell0->GetView();
2619 CPPUNIT_ASSERT(!pView0->GetTextEditPageView());
2622 namespace
2625 void lcl_extractHandleParameters(std::string_view selection, sal_uInt32& id, sal_uInt32& x, sal_uInt32& y)
2627 OString extraInfo( selection.substr(selection.find("{")) );
2628 std::stringstream aStream((std::string(extraInfo)));
2629 boost::property_tree::ptree aTree;
2630 boost::property_tree::read_json(aStream, aTree);
2631 boost::property_tree::ptree
2632 handle0 = aTree
2633 .get_child("handles")
2634 .get_child("kinds")
2635 .get_child("rectangle")
2636 .get_child("1")
2637 .begin()->second;
2638 id = handle0.get_child("id").get_value<int>();
2639 x = handle0.get_child("point").get_child("x").get_value<int>();
2640 y = handle0.get_child("point").get_child("y").get_value<int>();
2645 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testMoveShapeHandle)
2647 SdXImpressDocument* pXImpressDocument = createDoc("shape.odp");
2648 ViewCallback aView1;
2649 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
2650 SdPage* pPage = pViewShell->GetActualPage();
2651 SdrObject* pObject = pPage->GetObj(0);
2652 SdrView* pView = pViewShell->GetView();
2653 pView->MarkObj(pObject, pView->GetSdrPageView());
2654 Scheduler::ProcessEventsToIdle();
2656 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
2658 sal_uInt32 id, x, y;
2659 lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y);
2660 sal_uInt32 oldX = x;
2661 sal_uInt32 oldY = y;
2662 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
2664 {"HandleNum", uno::Any(id)},
2665 {"NewPosX", uno::Any(x+1)},
2666 {"NewPosY", uno::Any(y+1)}
2667 }));
2668 dispatchCommand(mxComponent, ".uno:MoveShapeHandle", aPropertyValues);
2669 CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty());
2670 lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y);
2671 CPPUNIT_ASSERT_EQUAL(x-1, oldX);
2672 CPPUNIT_ASSERT_EQUAL(y-1, oldY);
2676 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPasteUndo)
2678 // Given a document with a textbox, containing "world":
2679 SdXImpressDocument* pXImpressDocument = createDoc("paste-undo.fodp");
2680 sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
2681 SdPage* pActualPage = pViewShell->GetActualPage();
2682 SdrObject* pObject = pActualPage->GetObj(0);
2683 SdrView* pView = pViewShell->GetView();
2684 pView->MarkObj(pObject, pView->GetSdrPageView());
2685 pView->SdrBeginTextEdit(pObject);
2686 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME);
2687 pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME);
2688 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
2689 ESelection aWordSelection(0, 0, 0, 1); // "w" of "world"
2690 rEditView.SetSelection(aWordSelection);
2691 dispatchCommand(mxComponent, ".uno:Cut", {});
2693 // When undoing a paste:
2694 dispatchCommand(mxComponent, ".uno:Paste", {});
2695 dispatchCommand(mxComponent, ".uno:Undo", {});
2697 // Then make sure the cursor position is still at the beginning:
2698 ESelection aSelection = rEditView.GetSelection();
2699 // Without the accompanying fix in place, this test would have failed with:
2700 // - Expected: 0
2701 // - Actual : 4
2702 // i.e. the cursor position after undo was at the end of the line, not at the start, as
2703 // expected.
2704 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aSelection.nStartPos);
2707 CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testShapeEditInMultipleViews)
2709 SdXImpressDocument* pXImpressDocument = createDoc("TextBoxAndRect.odg");
2710 pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
2711 SdDrawDocument* pDocument = pXImpressDocument->GetDoc();
2713 // Create view 1
2714 const int nView1 = SfxLokHelper::getView();
2715 sd::ViewShell* pViewShell1 = pXImpressDocument->GetDocShell()->GetViewShell();
2716 SdrView* pView1 = pViewShell1->GetView();
2717 Scheduler::ProcessEventsToIdle();
2719 // Create view 2
2720 SfxLokHelper::createView();
2721 const int nView2 = SfxLokHelper::getView();
2722 CPPUNIT_ASSERT(nView1 != nView2);
2724 sd::ViewShell* pViewShell2 = pXImpressDocument->GetDocShell()->GetViewShell();
2725 SdrView* pView2 = pViewShell2->GetView();
2726 Scheduler::ProcessEventsToIdle();
2728 // Switch to view 1
2729 SfxLokHelper::setView(nView1);
2731 SdPage* pPage1 = pViewShell1->GetActualPage();
2733 SdrObject* pTextBoxObject = pPage1->GetObj(0);
2734 CPPUNIT_ASSERT_EQUAL(OUString("Text Box"), pTextBoxObject->GetName());
2736 SdrObject* pRectangleObject = pPage1->GetObj(1);
2737 CPPUNIT_ASSERT_EQUAL(OUString("Rect"), pRectangleObject->GetName());
2739 SdrObject* pTableObject = pPage1->GetObj(2);
2740 CPPUNIT_ASSERT_EQUAL(OUString("Table1"), pTableObject->GetName());
2742 // Scenario 1
2743 // 2 shapes - "Text Box" and "Rect"
2744 // View1 - "Text Box" enters text edit mode, View 2 - moves the "Rect" around
2746 sd::UndoManager* pUndoManager = pDocument->GetUndoManager();
2747 CPPUNIT_ASSERT_EQUAL(size_t(0), pUndoManager->GetUndoActionCount());
2749 pView1->SdrBeginTextEdit(pTextBoxObject);
2750 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2751 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2753 // Local undo count for View1 is 0
2754 CPPUNIT_ASSERT_EQUAL(size_t(0), pView1->getViewLocalUndoManager()->GetUndoActionCount());
2755 // Write 'test' in View1
2756 SfxStringItem aInputString(SID_ATTR_CHAR, "test");
2757 pViewShell1->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR, SfxCallMode::SYNCHRON, { &aInputString });
2758 // Local undo count for View1 is now 1
2759 CPPUNIT_ASSERT_EQUAL(size_t(1), pView1->getViewLocalUndoManager()->GetUndoActionCount());
2761 // Mark rectangle object
2762 pView2->MarkObj(pRectangleObject, pView2->GetSdrPageView());
2764 // Check the initial position of the object
2765 tools::Rectangle aRectangle = pRectangleObject->GetLogicRect();
2766 CPPUNIT_ASSERT_EQUAL(6250L, aRectangle.TopLeft().X());
2767 CPPUNIT_ASSERT_EQUAL(7000L, aRectangle.TopLeft().Y());
2768 CPPUNIT_ASSERT_EQUAL(6501L, aRectangle.GetWidth());
2769 CPPUNIT_ASSERT_EQUAL(4501L, aRectangle.GetHeight());
2771 // On View2 - Move handle 0 on the shape to a new position - resize
2772 Point aNewPosition = aRectangle.TopLeft() + Point(-1250, -1000);
2773 pView2->MoveShapeHandle(0, aNewPosition, -1);
2774 Scheduler::ProcessEventsToIdle();
2775 CPPUNIT_ASSERT_EQUAL(size_t(1), pUndoManager->GetUndoActionCount());
2777 // Check the object has a new size
2778 aRectangle = pRectangleObject->GetLogicRect();
2779 CPPUNIT_ASSERT_EQUAL(5000L, aRectangle.TopLeft().X());
2780 CPPUNIT_ASSERT_EQUAL(6000L, aRectangle.TopLeft().Y());
2781 CPPUNIT_ASSERT_EQUAL(7751L, aRectangle.GetWidth());
2782 CPPUNIT_ASSERT_EQUAL(5501L, aRectangle.GetHeight());
2784 // View1 is still in text edit mode...
2785 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2786 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2788 // On View2 - relative move the shape to a different position
2789 pView2->MoveMarkedObj(Size(1000, 2000), /*bCopy=*/false);
2790 Scheduler::ProcessEventsToIdle();
2791 CPPUNIT_ASSERT_EQUAL(size_t(2), pUndoManager->GetUndoActionCount());
2793 // Check the object is at a different position
2794 aRectangle = pRectangleObject->GetLogicRect();
2795 CPPUNIT_ASSERT_EQUAL(6000L, aRectangle.TopLeft().X());
2796 CPPUNIT_ASSERT_EQUAL(8000L, aRectangle.TopLeft().Y());
2797 CPPUNIT_ASSERT_EQUAL(7751L, aRectangle.GetWidth());
2798 CPPUNIT_ASSERT_EQUAL(5501L, aRectangle.GetHeight());
2800 // View1 is still in text edit mode...
2801 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2802 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2804 // End Text edit - check undo count increase from 2 -> 3
2805 CPPUNIT_ASSERT_EQUAL(size_t(2), pUndoManager->GetUndoActionCount());
2806 pView1->SdrEndTextEdit();
2807 Scheduler::ProcessEventsToIdle();
2808 CPPUNIT_ASSERT_EQUAL(size_t(3), pUndoManager->GetUndoActionCount());
2810 // Check that both views exited the text edit mode
2811 CPPUNIT_ASSERT_EQUAL(false, pView1->IsTextEdit());
2812 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2815 // Scenario 2
2816 // 1 shapes - "Text Box"
2817 // View1 - "Text Box" enters text edit mode, View 2 - moves the "Text Box" around
2819 sd::UndoManager* pUndoManager = pDocument->GetUndoManager();
2820 CPPUNIT_ASSERT_EQUAL(size_t(3), pUndoManager->GetUndoActionCount());
2822 pView1->SdrBeginTextEdit(pTextBoxObject);
2823 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2824 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2826 // Local undo count for View1 is 0
2827 CPPUNIT_ASSERT_EQUAL(size_t(0), pView1->getViewLocalUndoManager()->GetUndoActionCount());
2828 // Write 'test' in View1
2829 SfxStringItem aInputString(SID_ATTR_CHAR, "test");
2830 pViewShell1->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR, SfxCallMode::SYNCHRON, { &aInputString });
2831 // Local undo count for View1 is now 1
2832 CPPUNIT_ASSERT_EQUAL(size_t(1), pView1->getViewLocalUndoManager()->GetUndoActionCount());
2834 // Mark rectangle object
2835 pView2->MarkObj(pTextBoxObject, pView2->GetSdrPageView());
2837 // Check the initial position of the object
2838 tools::Rectangle aRectangle = pTextBoxObject->GetLogicRect();
2839 CPPUNIT_ASSERT_EQUAL(2250L, aRectangle.TopLeft().X());
2840 CPPUNIT_ASSERT_EQUAL(2000L, aRectangle.TopLeft().Y());
2841 CPPUNIT_ASSERT_EQUAL(4501L, aRectangle.GetWidth());
2842 CPPUNIT_ASSERT_EQUAL(2001L, aRectangle.GetHeight());
2844 // On View2 - Move handle 0 on the shape to a new position - resize
2845 Point aNewPosition = aRectangle.TopLeft() + Point(-1250, -1000);
2846 pView2->MoveShapeHandle(0, aNewPosition, -1);
2847 Scheduler::ProcessEventsToIdle();
2848 CPPUNIT_ASSERT_EQUAL(size_t(4), pUndoManager->GetUndoActionCount());
2850 // Check the object has a new size
2851 aRectangle = pTextBoxObject->GetLogicRect();
2852 CPPUNIT_ASSERT_EQUAL(1000L, aRectangle.TopLeft().X());
2853 CPPUNIT_ASSERT_EQUAL(1000L, aRectangle.TopLeft().Y());
2854 CPPUNIT_ASSERT_EQUAL(4990L, aRectangle.GetWidth());
2855 CPPUNIT_ASSERT_EQUAL(2175L, aRectangle.GetHeight());
2857 // View1 is still in text edit mode...
2858 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2859 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2861 // On View2 - relative move the shape to a different position
2862 pView2->MoveMarkedObj(Size(1000, 2000), /*bCopy=*/false);
2863 Scheduler::ProcessEventsToIdle();
2864 CPPUNIT_ASSERT_EQUAL(size_t(5), pUndoManager->GetUndoActionCount());
2866 // Check the object is at a different position
2867 aRectangle = pTextBoxObject->GetLogicRect();
2868 CPPUNIT_ASSERT_EQUAL(2000L, aRectangle.TopLeft().X());
2869 CPPUNIT_ASSERT_EQUAL(3000L, aRectangle.TopLeft().Y());
2870 CPPUNIT_ASSERT_EQUAL(4990L, aRectangle.GetWidth());
2871 CPPUNIT_ASSERT_EQUAL(2175L, aRectangle.GetHeight());
2873 // View1 is still in text edit mode...
2874 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2875 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2877 // End Text edit - check undo count increase from 5 -> 6
2878 CPPUNIT_ASSERT_EQUAL(size_t(5), pUndoManager->GetUndoActionCount());
2879 pView1->SdrEndTextEdit();
2880 Scheduler::ProcessEventsToIdle();
2881 CPPUNIT_ASSERT_EQUAL(size_t(6), pUndoManager->GetUndoActionCount());
2883 // Check that both views exited the text edit mode
2884 CPPUNIT_ASSERT_EQUAL(false, pView1->IsTextEdit());
2885 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2888 // Scenario 3
2889 // 1 shapes - "Table1"
2890 // View1 - "Table1" enters text edit mode, View 2 - moves the "Table1" around
2892 sd::UndoManager* pUndoManager = pDocument->GetUndoManager();
2893 CPPUNIT_ASSERT_EQUAL(size_t(6), pUndoManager->GetUndoActionCount());
2895 pView1->SdrBeginTextEdit(pTableObject);
2896 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2897 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2899 // Local undo count for View1 is 0
2900 CPPUNIT_ASSERT_EQUAL(size_t(0), pView1->getViewLocalUndoManager()->GetUndoActionCount());
2901 // Write 'test' in View1
2902 SfxStringItem aInputString(SID_ATTR_CHAR, "test");
2903 pViewShell1->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_CHAR, SfxCallMode::SYNCHRON, { &aInputString });
2904 // Local undo count for View1 is now 1
2905 CPPUNIT_ASSERT_EQUAL(size_t(1), pView1->getViewLocalUndoManager()->GetUndoActionCount());
2907 // Mark rectangle object
2908 pView2->MarkObj(pTableObject, pView2->GetSdrPageView());
2910 // Check the initial position of the table
2911 tools::Rectangle aRectangle = pTableObject->GetLogicRect();
2912 CPPUNIT_ASSERT_EQUAL(2919L, aRectangle.TopLeft().X());
2913 CPPUNIT_ASSERT_EQUAL(18063L, aRectangle.TopLeft().Y());
2914 CPPUNIT_ASSERT_EQUAL(14099L, aRectangle.GetWidth());
2915 CPPUNIT_ASSERT_EQUAL(5999L, aRectangle.GetHeight());
2917 // On View2 - relative move the shape to a different position
2918 pView2->MoveMarkedObj(Size(1000, 2000), /*bCopy=*/false);
2919 Scheduler::ProcessEventsToIdle();
2920 CPPUNIT_ASSERT_EQUAL(size_t(7), pUndoManager->GetUndoActionCount());
2922 // Check the object is at a different position
2923 aRectangle = pTableObject->GetLogicRect();
2924 CPPUNIT_ASSERT_EQUAL(3919L, aRectangle.TopLeft().X());
2925 CPPUNIT_ASSERT_EQUAL(20063L, aRectangle.TopLeft().Y());
2926 CPPUNIT_ASSERT_EQUAL(14099L, aRectangle.GetWidth());
2927 CPPUNIT_ASSERT_EQUAL(5999L, aRectangle.GetHeight());
2929 // View1 is still in text edit mode...
2930 CPPUNIT_ASSERT_EQUAL(true, pView1->IsTextEdit());
2931 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2933 // End Text edit - check undo count increase from 7 -> 8
2934 CPPUNIT_ASSERT_EQUAL(size_t(7), pUndoManager->GetUndoActionCount());
2935 pView1->SdrEndTextEdit();
2936 Scheduler::ProcessEventsToIdle();
2937 CPPUNIT_ASSERT_EQUAL(size_t(8), pUndoManager->GetUndoActionCount());
2939 // Check that both views exited the text edit mode
2940 CPPUNIT_ASSERT_EQUAL(false, pView1->IsTextEdit());
2941 CPPUNIT_ASSERT_EQUAL(false, pView2->IsTextEdit());
2945 CPPUNIT_PLUGIN_IMPLEMENT();
2947 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */