1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <com/sun/star/frame/Desktop.hpp>
12 #include <com/sun/star/lang/XComponent.hpp>
13 #include <com/sun/star/text/XTextDocument.hpp>
14 #include <com/sun/star/awt/Key.hpp>
15 #include <com/sun/star/awt/XReschedule.hpp>
16 #include <com/sun/star/awt/Toolkit.hpp>
17 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
18 #include <com/sun/star/util/XModifiable.hpp>
19 #include <com/sun/star/text/TextContentAnchorType.hpp>
20 #include <boost/property_tree/json_parser.hpp>
22 #include <com/sun/star/frame/XStorable.hpp>
24 #include <vcl/combobox.hxx>
25 #include <vcl/scheduler.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/syswin.hxx>
28 #include <vcl/window.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <rtl/uri.hxx>
31 #include <sfx2/objsh.hxx>
32 #include <sfx2/lokhelper.hxx>
33 #include <test/unoapi_test.hxx>
34 #include <comphelper/lok.hxx>
35 #include <comphelper/dispatchcommand.hxx>
36 #include <comphelper/propertysequence.hxx>
37 #include <osl/conditn.hxx>
38 #include <svl/srchitem.hxx>
39 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
40 #include <unotools/tempfile.hxx>
41 #include <sfx2/viewsh.hxx>
42 #include <sfx2/viewfrm.hxx>
43 #include <sfx2/bindings.hxx>
44 #include <unotools/datetime.hxx>
45 #include <unotools/syslocaleoptions.hxx>
46 #include <comphelper/string.hxx>
47 #include <comphelper/scopeguard.hxx>
50 #include <config_features.h>
52 #include <lib/init.hxx>
54 using namespace com::sun::star
;
55 using namespace desktop
;
57 class DesktopLOKTest
: public UnoApiTest
62 std::function
<void ()> m_Func
;
65 Resetter(std::function
<void ()> const& rFunc
)
75 catch (...) // has to be reliable
77 fprintf(stderr
, "resetter failed with exception\n");
84 DesktopLOKTest() : UnoApiTest("/desktop/qa/data/"),
85 m_nSelectionBeforeSearchResult(0),
86 m_nSelectionAfterSearchResult(0),
92 void readFileIntoByteVector(OUString
const & sFilename
, std::vector
<sal_uInt8
> & rByteVector
);
94 virtual void setUp() override
96 comphelper::LibreOfficeKit::setActive(true);
100 mxDesktop
.set(frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
101 SfxApplication::GetOrCreate();
104 virtual void tearDown() override
107 m_pDocument
->pClass
->registerCallback(m_pDocument
.get(), nullptr, nullptr);
110 UnoApiTest::tearDown();
112 comphelper::LibreOfficeKit::setActive(false);
115 LibLODocument_Impl
* loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
116 LibLODocument_Impl
* loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
= LOK_DOCTYPE_TEXT
);
118 static void callback(int nType
, const char* pPayload
, void* pData
);
119 void callbackImpl(int nType
, const char* pPayload
);
121 void testGetStyles();
123 void testCreateView();
124 void testGetFilterTypes();
125 void testGetPartPageRectangles();
126 void testSearchCalc();
127 void testSearchAllNotificationsCalc();
128 void testPaintTile();
130 void testSaveAsCalc();
131 void testPasteWriter();
132 void testPasteWriterJPEG();
133 void testUndoWriter();
134 void testRowColumnHeaders();
135 void testHiddenRowHeaders();
136 void testCellCursor();
137 void testCommandResult();
138 void testWriterComments();
139 void testSheetOperations();
140 void testSheetSelections();
141 void testContextMenuCalc();
142 void testContextMenuWriter();
143 void testContextMenuImpress();
144 void testNotificationCompression();
145 void testTileInvalidationCompression();
146 void testPartInInvalidation();
148 void testRedlineWriter();
149 void testTrackChanges();
150 void testRedlineCalc();
151 void testPaintPartTile();
152 void testWriterCommentInsertCursor();
154 void testGetFontSubset();
156 void testCommentsWriter();
157 void testCommentsCalc();
158 void testCommentsImpress();
159 void testCommentsCallbacksWriter();
161 void testExtractParameter();
162 void testGetSignatureState_NonSigned();
163 void testGetSignatureState_Signed();
164 void testInsertCertificate_DER_ODT();
165 void testInsertCertificate_PEM_ODT();
166 void testInsertCertificate_PEM_DOCX();
167 void testSignDocument_PEM_PDF();
168 void testTextSelectionHandles();
169 void testComplexSelection();
170 void testSpellcheckerMultiView();
171 void testDialogPaste();
172 void testCalcSaveAs();
173 void testDialogInput();
176 CPPUNIT_TEST_SUITE(DesktopLOKTest
);
177 CPPUNIT_TEST(testGetStyles
);
178 CPPUNIT_TEST(testGetFonts
);
179 CPPUNIT_TEST(testCreateView
);
180 CPPUNIT_TEST(testGetFilterTypes
);
181 CPPUNIT_TEST(testGetPartPageRectangles
);
182 CPPUNIT_TEST(testSearchCalc
);
183 CPPUNIT_TEST(testSearchAllNotificationsCalc
);
184 CPPUNIT_TEST(testPaintTile
);
185 CPPUNIT_TEST(testSaveAs
);
186 CPPUNIT_TEST(testSaveAsCalc
);
187 CPPUNIT_TEST(testPasteWriter
);
188 CPPUNIT_TEST(testPasteWriterJPEG
);
189 CPPUNIT_TEST(testUndoWriter
);
190 CPPUNIT_TEST(testRowColumnHeaders
);
191 CPPUNIT_TEST(testHiddenRowHeaders
);
192 CPPUNIT_TEST(testCellCursor
);
193 CPPUNIT_TEST(testCommandResult
);
194 CPPUNIT_TEST(testWriterComments
);
195 CPPUNIT_TEST(testSheetOperations
);
196 CPPUNIT_TEST(testSheetSelections
);
197 CPPUNIT_TEST(testContextMenuCalc
);
198 CPPUNIT_TEST(testContextMenuWriter
);
199 CPPUNIT_TEST(testContextMenuImpress
);
200 CPPUNIT_TEST(testNotificationCompression
);
201 CPPUNIT_TEST(testTileInvalidationCompression
);
202 CPPUNIT_TEST(testPartInInvalidation
);
203 CPPUNIT_TEST(testInput
);
204 CPPUNIT_TEST(testRedlineWriter
);
205 CPPUNIT_TEST(testTrackChanges
);
206 CPPUNIT_TEST(testRedlineCalc
);
207 CPPUNIT_TEST(testPaintPartTile
);
208 CPPUNIT_TEST(testWriterCommentInsertCursor
);
210 CPPUNIT_TEST(testGetFontSubset
);
212 CPPUNIT_TEST(testCommentsWriter
);
213 CPPUNIT_TEST(testCommentsCalc
);
214 CPPUNIT_TEST(testCommentsImpress
);
215 CPPUNIT_TEST(testCommentsCallbacksWriter
);
216 CPPUNIT_TEST(testRunMacro
);
217 CPPUNIT_TEST(testExtractParameter
);
218 CPPUNIT_TEST(testGetSignatureState_Signed
);
219 CPPUNIT_TEST(testGetSignatureState_NonSigned
);
220 CPPUNIT_TEST(testInsertCertificate_DER_ODT
);
221 CPPUNIT_TEST(testInsertCertificate_PEM_ODT
);
222 CPPUNIT_TEST(testInsertCertificate_PEM_DOCX
);
223 CPPUNIT_TEST(testSignDocument_PEM_PDF
);
224 CPPUNIT_TEST(testTextSelectionHandles
);
225 CPPUNIT_TEST(testComplexSelection
);
226 CPPUNIT_TEST(testSpellcheckerMultiView
);
227 CPPUNIT_TEST(testDialogPaste
);
228 CPPUNIT_TEST(testCalcSaveAs
);
229 CPPUNIT_TEST(testDialogInput
);
230 CPPUNIT_TEST(testABI
);
231 CPPUNIT_TEST_SUITE_END();
233 uno::Reference
<lang::XComponent
> mxComponent
;
234 OString m_aTextSelection
;
235 OString m_aTextSelectionStart
;
236 OString m_aTextSelectionEnd
;
237 std::vector
<OString
> m_aSearchResultSelection
;
238 std::vector
<int> m_aSearchResultPart
;
239 int m_nSelectionBeforeSearchResult
;
240 int m_nSelectionAfterSearchResult
;
242 // for testCommandResult
243 osl::Condition m_aCommandResultCondition
;
244 OString m_aCommandResult
;
246 // for testModifiedStatus
247 osl::Condition m_aStateChangedCondition
;
251 // for testContextMenu{Calc, Writer}
252 osl::Condition m_aContextMenuCondition
;
253 boost::property_tree::ptree m_aContextMenuResult
;
255 std::unique_ptr
<LibLODocument_Impl
> m_pDocument
;
258 static Control
* GetFocusControl(vcl::Window
const * pParent
)
260 sal_uInt16 nChildren
= pParent
->GetChildCount();
261 for (sal_uInt16 nChild
= 0; nChild
< nChildren
; ++nChild
)
263 vcl::Window
* pChild
= pParent
->GetChild( nChild
);
264 Control
* pCtrl
= dynamic_cast<Control
*>(pChild
);
265 if (pCtrl
&& pCtrl
->HasControlFocus())
268 Control
* pSubCtrl
= GetFocusControl( pChild
);
275 LibLODocument_Impl
* DesktopLOKTest::loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
280 case LOK_DOCTYPE_TEXT
:
281 aService
= "com.sun.star.text.TextDocument";
283 case LOK_DOCTYPE_SPREADSHEET
:
284 aService
= "com.sun.star.sheet.SpreadsheetDocument";
286 case LOK_DOCTYPE_PRESENTATION
:
287 aService
= "com.sun.star.presentation.PresentationDocument";
290 CPPUNIT_ASSERT(false);
293 mxComponent
= loadFromDesktop(rFileURL
, aService
);
294 if (!mxComponent
.is())
296 CPPUNIT_ASSERT(false);
298 m_pDocument
.reset(new LibLODocument_Impl(mxComponent
));
299 return m_pDocument
.get();
302 LibLODocument_Impl
* DesktopLOKTest::loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
)
305 createFileURL(OUString::createFromAscii(pName
), aFileURL
);
306 return loadDocUrl(aFileURL
, eType
);
309 void DesktopLOKTest::closeDoc()
311 if (mxComponent
.is())
313 closeDocument(mxComponent
);
318 void DesktopLOKTest::callback(int nType
, const char* pPayload
, void* pData
)
320 static_cast<DesktopLOKTest
*>(pData
)->callbackImpl(nType
, pPayload
);
323 void DesktopLOKTest::callbackImpl(int nType
, const char* pPayload
)
327 case LOK_CALLBACK_TEXT_SELECTION
:
329 m_aTextSelection
= pPayload
;
330 if (m_aSearchResultSelection
.empty())
331 ++m_nSelectionBeforeSearchResult
;
333 ++m_nSelectionAfterSearchResult
;
336 case LOK_CALLBACK_TEXT_SELECTION_START
:
337 m_aTextSelectionStart
= pPayload
;
339 case LOK_CALLBACK_TEXT_SELECTION_END
:
340 m_aTextSelectionEnd
= pPayload
;
342 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
344 m_aSearchResultSelection
.clear();
345 boost::property_tree::ptree aTree
;
346 std::stringstream
aStream(pPayload
);
347 boost::property_tree::read_json(aStream
, aTree
);
348 for (boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("searchResultSelection"))
350 m_aSearchResultSelection
.emplace_back(rValue
.second
.get
<std::string
>("rectangles").c_str());
351 m_aSearchResultPart
.push_back(std::atoi(rValue
.second
.get
<std::string
>("part").c_str()));
355 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
357 m_aCommandResult
= pPayload
;
358 m_aCommandResultCondition
.set();
361 case LOK_CALLBACK_STATE_CHANGED
:
363 OString
aPayload(pPayload
);
364 OString
aPrefix(".uno:ModifiedStatus=");
365 if (aPayload
.startsWith(aPrefix
))
367 m_bModified
= aPayload
.copy(aPrefix
.getLength()).toBoolean();
368 m_aStateChangedCondition
.set();
370 else if (aPayload
.startsWith(".uno:TrackChanges=") && aPayload
.endsWith("=true"))
374 case LOK_CALLBACK_CONTEXT_MENU
:
376 m_aContextMenuResult
.clear();
377 std::stringstream
aStream(pPayload
);
378 boost::property_tree::read_json(aStream
, m_aContextMenuResult
);
379 m_aContextMenuCondition
.set();
385 void DesktopLOKTest::testGetStyles()
387 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
388 boost::property_tree::ptree aTree
;
389 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:StyleApply");
390 std::stringstream
aStream(pJSON
);
391 boost::property_tree::read_json(aStream
, aTree
);
392 CPPUNIT_ASSERT( !aTree
.empty() );
393 CPPUNIT_ASSERT_EQUAL( std::string(".uno:StyleApply"), aTree
.get_child("commandName").get_value
<std::string
>() );
395 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
396 CPPUNIT_ASSERT( !aValues
.empty() );
397 for (const auto& rPair
: aValues
)
399 if( rPair
.first
!= "ClearStyle")
401 CPPUNIT_ASSERT( !rPair
.second
.empty());
403 if (rPair
.first
!= "CharacterStyles" &&
404 rPair
.first
!= "ParagraphStyles" &&
405 rPair
.first
!= "FrameStyles" &&
406 rPair
.first
!= "PageStyles" &&
407 rPair
.first
!= "NumberingStyles" &&
408 rPair
.first
!= "CellStyles" &&
409 rPair
.first
!= "ShapeStyles" &&
410 rPair
.first
!= "TableStyles" &&
411 rPair
.first
!= "HeaderFooter" &&
412 rPair
.first
!= "Commands")
414 CPPUNIT_FAIL("Unknown style family: " + rPair
.first
);
419 void DesktopLOKTest::testGetFonts()
421 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
422 boost::property_tree::ptree aTree
;
423 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CharFontName");
424 std::stringstream
aStream(pJSON
);
425 boost::property_tree::read_json(aStream
, aTree
);
426 CPPUNIT_ASSERT( !aTree
.empty() );
427 CPPUNIT_ASSERT_EQUAL( std::string(".uno:CharFontName"), aTree
.get_child("commandName").get_value
<std::string
>() );
429 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
430 CPPUNIT_ASSERT( !aValues
.empty() );
431 for (const auto& rPair
: aValues
)
433 // check that we have font sizes available for each font
434 CPPUNIT_ASSERT( !rPair
.second
.empty());
439 void DesktopLOKTest::testCreateView()
441 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
442 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
444 int nId0
= pDocument
->m_pDocumentClass
->getView(pDocument
);
445 int nId1
= pDocument
->m_pDocumentClass
->createView(pDocument
);
446 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
448 // Test getViewIds().
449 std::vector
<int> aViewIds(2);
450 CPPUNIT_ASSERT(pDocument
->m_pDocumentClass
->getViewIds(pDocument
, aViewIds
.data(), aViewIds
.size()));
451 CPPUNIT_ASSERT_EQUAL(nId0
, aViewIds
[0]);
452 CPPUNIT_ASSERT_EQUAL(nId1
, aViewIds
[1]);
454 // Make sure the created view is the active one, then switch to the old
456 CPPUNIT_ASSERT_EQUAL(nId1
, pDocument
->m_pDocumentClass
->getView(pDocument
));
457 pDocument
->m_pDocumentClass
->setView(pDocument
, nId0
);
458 CPPUNIT_ASSERT_EQUAL(nId0
, pDocument
->m_pDocumentClass
->getView(pDocument
));
460 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nId1
);
461 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
464 void DesktopLOKTest::testGetPartPageRectangles()
466 // Test that we get as many page rectangles as expected: blank document is
468 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
469 char* pRectangles
= pDocument
->pClass
->getPartPageRectangles(pDocument
);
470 OUString sRectangles
= OUString::fromUtf8(pRectangles
);
472 std::vector
<OUString
> aRectangles
;
473 sal_Int32 nIndex
= 0;
476 OUString aRectangle
= sRectangles
.getToken(0, ';', nIndex
);
477 if (!aRectangle
.isEmpty())
478 aRectangles
.push_back(aRectangle
);
481 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aRectangles
.size());
486 void DesktopLOKTest::testGetFilterTypes()
488 LibLibreOffice_Impl aOffice
;
489 char* pJSON
= aOffice
.m_pOfficeClass
->getFilterTypes(&aOffice
);
491 std::stringstream
aStream(pJSON
);
492 boost::property_tree::ptree aTree
;
493 boost::property_tree::read_json(aStream
, aTree
);
495 CPPUNIT_ASSERT(!aTree
.empty());
496 CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.oasis.opendocument.text"), aTree
.get_child("writer8").get_child("MediaType").get_value
<std::string
>());
500 void DesktopLOKTest::testSearchCalc()
502 LibLibreOffice_Impl aOffice
;
503 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
504 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
505 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
507 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
509 {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
510 {"SearchItem.Backward", uno::makeAny(false)},
511 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
513 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues
);
514 Scheduler::ProcessEventsToIdle();
516 std::vector
<OString
> aSelections
;
517 sal_Int32 nIndex
= 0;
520 OString aToken
= m_aTextSelection
.getToken(0, ';', nIndex
);
521 aSelections
.push_back(aToken
);
522 } while (nIndex
>= 0);
523 // This was 1, find-all only found one match.
524 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aSelections
.size());
525 // Make sure that we get exactly as many rectangle lists as matches.
526 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), m_aSearchResultSelection
.size());
527 // Result is on the first sheet.
528 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart
[0]);
531 void DesktopLOKTest::testSearchAllNotificationsCalc()
533 LibLibreOffice_Impl aOffice
;
534 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
535 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
536 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
538 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
540 {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
541 {"SearchItem.Backward", uno::makeAny(false)},
542 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
544 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues
);
545 Scheduler::ProcessEventsToIdle();
547 // This was 1, make sure that we get no notifications about selection changes during search.
548 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult
);
549 // But we do get the selection afterwards.
550 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult
> 0);
553 void DesktopLOKTest::testPaintTile()
555 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
556 int nCanvasWidth
= 100;
557 int nCanvasHeight
= 300;
558 sal_Int32 nStride
= cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32
, nCanvasWidth
);
559 std::vector
<unsigned char> aBuffer(nStride
* nCanvasHeight
);
562 int nTileWidth
= 1000;
563 int nTileHeight
= 3000;
565 // This used to crash: paintTile() implementation did not handle
566 // nCanvasWidth != nCanvasHeight correctly, as usually both are just always
568 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
570 // This crashed in OutputDevice::DrawDeviceAlphaBitmap().
575 aBuffer
.resize(nCanvasWidth
* nCanvasHeight
* 4);
576 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
579 void DesktopLOKTest::testSaveAs()
581 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
582 utl::TempFile aTempFile
;
583 aTempFile
.EnableKillingFile();
584 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
587 void DesktopLOKTest::testSaveAsCalc()
589 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
590 utl::TempFile aTempFile
;
591 aTempFile
.EnableKillingFile();
592 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
595 void DesktopLOKTest::testPasteWriter()
597 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
598 OString
aText("hello");
600 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
602 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
603 Scheduler::ProcessEventsToIdle();
604 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
605 CPPUNIT_ASSERT_EQUAL(OString("hello"), OString(pText
));
608 // textt/plain should be rejected.
609 CPPUNIT_ASSERT(!pDocument
->pClass
->paste(pDocument
, "textt/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
610 // Writer is expected to support text/html.
611 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aText
.getStr(), aText
.getLength()));
613 // Overwrite doc contents with a HTML paste.
614 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
615 Scheduler::ProcessEventsToIdle();
616 OString
aComment("foo <!-- bar --> baz");
617 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aComment
.getStr(), aComment
.getLength()));
619 // Check if we have a comment.
620 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
621 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
622 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
623 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
624 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
625 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
626 CPPUNIT_ASSERT_EQUAL(OUString("Text"), xTextPortion
->getPropertyValue("TextPortionType").get
<OUString
>());
627 // Without the accompanying fix in place, this test would have failed, as we had a comment
628 // between "foo" and "baz".
629 CPPUNIT_ASSERT(!xTextPortionEnumeration
->hasMoreElements());
632 void DesktopLOKTest::testPasteWriterJPEG()
634 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
637 createFileURL("paste.jpg", aFileURL
);
638 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
639 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
641 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
643 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
644 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
645 // This was 0, JPEG was not handled as a format for clipboard paste.
646 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(1), xDrawPage
->getCount());
648 uno::Reference
<beans::XPropertySet
> xShape(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
649 // This was text::TextContentAnchorType_AT_PARAGRAPH.
650 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER
, xShape
->getPropertyValue("AnchorType").get
<text::TextContentAnchorType
>());
652 // Delete the pasted picture, and paste again with a custom anchor type.
653 uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY
)->dispose();
654 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
656 {"AnchorType", uno::makeAny(static_cast<sal_uInt16
>(text::TextContentAnchorType_AT_CHARACTER
))},
658 comphelper::dispatchCommand(".uno:Paste", aPropertyValues
);
659 xShape
.set(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
660 // This was text::TextContentAnchorType_AS_CHARACTER, AnchorType argument was ignored.
661 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AT_CHARACTER
, xShape
->getPropertyValue("AnchorType").get
<text::TextContentAnchorType
>());
664 void DesktopLOKTest::testUndoWriter()
666 // Load a Writer document and press a key.
667 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
668 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
669 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
670 Scheduler::ProcessEventsToIdle();
672 boost::property_tree::ptree aTree
;
673 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:Undo");
674 std::stringstream
aStream(pJSON
);
676 CPPUNIT_ASSERT(!aStream
.str().empty());
677 boost::property_tree::read_json(aStream
, aTree
);
678 // Make sure that pressing a key creates exactly one undo action.
679 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("actions").size());
682 void DesktopLOKTest::testRowColumnHeaders()
690 * "size": "254.987250637468",
694 * "size": "509.974501274936",
700 * "size": "1274.93625318734",
704 * "size": "2549.87250637468",
710 * "size" defines the bottom/right boundary of a row/column in twips (size between 0 and boundary)
711 * "text" has the header label in UTF-8
713 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
715 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
719 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
720 long nX
= rtl::math::round(nWidth
/ 4.0);
721 long nY
= rtl::math::round(nHeight
/ 4.0);
722 nWidth
= rtl::math::round(nWidth
/ 2.0);
723 nHeight
= rtl::math::round(nHeight
/ 2.0);
725 std::stringstream aPayload
;
726 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
728 boost::property_tree::ptree aTree
;
729 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
730 std::stringstream
aStream(pJSON
);
733 CPPUNIT_ASSERT(!aStream
.str().empty());
735 boost::property_tree::read_json(aStream
, aTree
);
736 sal_Int32 nPrevious
= 0;
737 bool bFirstHeader
= true;
738 bool bNotEnoughHeaders
= true;
739 for (boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
741 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
742 nSize
*= 15; /* TWIPS_PER_PIXEL */
743 OString
aText(rValue
.second
.get
<std::string
>("text").c_str());
747 CPPUNIT_ASSERT(nSize
<= nY
);
748 CPPUNIT_ASSERT_EQUAL(OString("10"), aText
);
749 bFirstHeader
= false;
753 CPPUNIT_ASSERT(nSize
> 0);
754 CPPUNIT_ASSERT(nPrevious
< nSize
);
755 if (nSize
> nY
+ nHeight
)
757 bNotEnoughHeaders
= false;
763 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
767 bNotEnoughHeaders
= true;
768 for (boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("columns"))
770 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
771 nSize
*= 15; /* TWIPS_PER_PIXEL */
772 OString
aText(rValue
.second
.get
<std::string
>("text").c_str());
775 CPPUNIT_ASSERT(nSize
<= nX
);
776 CPPUNIT_ASSERT_EQUAL(OString("3"), aText
);
777 bFirstHeader
= false;
781 CPPUNIT_ASSERT(nSize
> 0);
782 CPPUNIT_ASSERT(nPrevious
< nSize
);
783 if (nSize
> nX
+ nWidth
)
785 bNotEnoughHeaders
= false;
791 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
794 void DesktopLOKTest::testHiddenRowHeaders()
796 LibLODocument_Impl
* pDocument
= loadDoc("hidden-row.ods");
798 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
804 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
806 std::stringstream aPayload
;
807 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
809 boost::property_tree::ptree aTree
;
810 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
811 std::stringstream
aStream(pJSON
);
813 CPPUNIT_ASSERT(!aStream
.str().empty());
815 boost::property_tree::read_json(aStream
, aTree
);
816 sal_Int32 nPrevious
= 0;
817 sal_Int32 nIndex
= 0;
818 for (boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
820 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
824 // nSize was 510, nPrevious was 255, i.e. hidden row wasn't reported as 0 height.
825 CPPUNIT_ASSERT_EQUAL(nPrevious
, nSize
);
832 void DesktopLOKTest::testCellCursor()
834 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
836 boost::property_tree::ptree aTree
;
838 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CellCursor?tileWidth=1&tileHeight=1&outputWidth=1&outputHeight=1");
840 std::stringstream
aStream(pJSON
);
842 CPPUNIT_ASSERT(!aStream
.str().empty());
844 boost::property_tree::read_json(aStream
, aTree
);
846 OString
aRectangle(aTree
.get
<std::string
>("commandValues").c_str());
847 // cell cursor geometry + col + row
848 CPPUNIT_ASSERT_EQUAL(OString("0, 0, 1274, 254, 0, 0"), aRectangle
);
851 void DesktopLOKTest::testCommandResult()
853 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
855 // the postUnoCommand() is supposed to be async, let's test it safely
856 // [no idea if it is async in reality - most probably we are operating
857 // under some solar mutex or something anyway ;-) - but...]
858 TimeValue aTimeValue
= { 2 , 0 }; // 2 seconds max
860 // nothing is triggered when we have no callback yet, we just time out on
861 // the condition var.
862 m_aCommandResultCondition
.reset();
863 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
864 Scheduler::ProcessEventsToIdle();
865 m_aCommandResultCondition
.wait(aTimeValue
);
867 CPPUNIT_ASSERT(m_aCommandResult
.isEmpty());
869 // but we get some real values when the callback is set up
870 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
872 m_aCommandResultCondition
.reset();
873 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
874 Scheduler::ProcessEventsToIdle();
875 m_aCommandResultCondition
.wait(aTimeValue
);
877 boost::property_tree::ptree aTree
;
878 std::stringstream
aStream(m_aCommandResult
.getStr());
879 boost::property_tree::read_json(aStream
, aTree
);
881 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), aTree
.get_child("commandName").get_value
<std::string
>());
882 CPPUNIT_ASSERT_EQUAL(true, aTree
.get_child("success").get_value
<bool>());
885 void DesktopLOKTest::testWriterComments()
887 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
888 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
889 uno::Reference
<awt::XReschedule
> xToolkit(com::sun::star::awt::Toolkit::create(comphelper::getProcessComponentContext()), uno::UNO_QUERY
);
891 // Insert a comment at the beginning of the document and wait till the main
892 // loop grabs the focus, so characters end up in the annotation window.
893 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
894 m_aCommandResultCondition
.reset();
895 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", nullptr, true);
896 Scheduler::ProcessEventsToIdle();
897 m_aCommandResultCondition
.wait(aTimeValue
);
898 CPPUNIT_ASSERT(!m_aCommandResult
.isEmpty());
899 xToolkit
->reschedule();
901 // Test that we have a comment.
902 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
903 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
904 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
905 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
906 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
907 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
908 CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), xTextPortion
->getPropertyValue("TextPortionType").get
<OUString
>());
910 // Type "test" and finish editing.
911 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
912 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'e', 0);
913 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 's', 0);
914 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
915 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
916 Scheduler::ProcessEventsToIdle();
918 // Test that the typed characters ended up in the right window.
919 auto xTextField
= xTextPortion
->getPropertyValue("TextField").get
< uno::Reference
<beans::XPropertySet
> >();
920 // This was empty, typed characters ended up in the body text.
921 CPPUNIT_ASSERT_EQUAL(OUString("test"), xTextField
->getPropertyValue("Content").get
<OUString
>());
924 void DesktopLOKTest::testTrackChanges()
926 // Load a document and create two views.
927 LibLibreOffice_Impl aOffice
;
928 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
929 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
930 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
931 pDocument
->pClass
->createView(pDocument
);
932 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
933 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
934 Scheduler::ProcessEventsToIdle();
936 // Enable trak changes and assert that both views get notified.
938 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:TrackChanges", nullptr, false);
939 Scheduler::ProcessEventsToIdle();
940 // This was 1, only the active view was notified.
941 CPPUNIT_ASSERT_EQUAL(2, m_nTrackChanges
);
944 void DesktopLOKTest::testSheetOperations()
946 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
948 // insert the last sheet
949 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
950 "{ \"Name\": { \"type\": \"string\", \"value\": \"LastSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 0 } }", false);
952 // insert the first sheet
953 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
954 "{ \"Name\": { \"type\": \"string\", \"value\": \"FirstSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 1 } }", false);
956 // rename the \"Sheet1\" (2nd now) to \"Renamed\"
957 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Name",
958 "{ \"Name\": { \"type\": \"string\", \"value\": \"Renamed\" }, \"Index\": { \"type\": \"long\", \"value\": 2 } }", false);
960 // delete the \"Sheet2\" (3rd)
961 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Remove",
962 "{ \"Index\": { \"type\": \"long\", \"value\": 3 } }", false);
964 Scheduler::ProcessEventsToIdle();
965 CPPUNIT_ASSERT_EQUAL(6, pDocument
->pClass
->getParts(pDocument
));
967 std::vector
<OString
> aExpected
= { "FirstSheet", "Renamed", "Sheet3", "Sheet4", "Sheet5", "LastSheet" };
968 for (int i
= 0; i
< 6; ++i
)
970 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], OString(pDocument
->pClass
->getPartName(pDocument
, i
)));
974 void DesktopLOKTest::testSheetSelections()
976 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods", LOK_DOCTYPE_SPREADSHEET
);
977 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
978 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
981 * Check if selection data is correct
986 int const col2
= 2200;
987 int const col3
= 3300;
991 // Select row 5 from column 1 through column 5
992 pDocument
->pClass
->postMouseEvent(pDocument
,
993 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
996 pDocument
->pClass
->postMouseEvent(pDocument
,
997 LOK_MOUSEEVENT_MOUSEMOVE
,
1000 pDocument
->pClass
->postMouseEvent(pDocument
,
1001 LOK_MOUSEEVENT_MOUSEMOVE
,
1004 pDocument
->pClass
->postMouseEvent(pDocument
,
1005 LOK_MOUSEEVENT_MOUSEMOVE
,
1008 pDocument
->pClass
->postMouseEvent(pDocument
,
1009 LOK_MOUSEEVENT_MOUSEMOVE
,
1012 pDocument
->pClass
->postMouseEvent(pDocument
,
1013 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1016 Scheduler::ProcessEventsToIdle();
1018 // Copy the contents and check if matches expected data
1020 char* pUsedMimeType
= nullptr;
1021 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1022 std::vector
<long> aExpected
= {5, 6, 7, 8, 9};
1023 std::istringstream
iss(pCopiedContent
);
1024 for (size_t i
= 0; i
< aExpected
.size(); i
++)
1028 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], strtol(token
.c_str(), nullptr, 10));
1031 free(pUsedMimeType
);
1032 free(pCopiedContent
);
1036 * Check if clicking inside the selection deselects the whole selection
1038 int const row10
= 2400;
1039 // Select starting from row5, col1 to row10, col5
1040 pDocument
->pClass
->postMouseEvent(pDocument
,
1041 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1044 pDocument
->pClass
->postMouseEvent(pDocument
,
1045 LOK_MOUSEEVENT_MOUSEMOVE
,
1048 pDocument
->pClass
->postMouseEvent(pDocument
,
1049 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1053 // Click at row5, col4
1054 pDocument
->pClass
->postMouseEvent(pDocument
,
1055 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1058 pDocument
->pClass
->postMouseEvent(pDocument
,
1059 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1062 Scheduler::ProcessEventsToIdle();
1064 // Selected text should get deselected and copying should give us
1065 // content of only one cell, now
1067 char* pUsedMimeType
= nullptr;
1068 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1069 std::vector
<long> aExpected
= { 8 };
1070 std::istringstream
iss(pCopiedContent
);
1071 for (size_t i
= 0; i
< aExpected
.size(); i
++)
1075 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], strtol(token
.c_str(), nullptr, 10));
1078 free(pUsedMimeType
);
1079 free(pCopiedContent
);
1085 void verifyContextMenuStructure(boost::property_tree::ptree
& aRoot
)
1087 for (const auto& aItemPair
: aRoot
)
1089 // This is an array, so no key
1090 CPPUNIT_ASSERT_EQUAL(std::string(aItemPair
.first
), std::string(""));
1092 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1093 boost::optional
<boost::property_tree::ptree
&> aText
= aItemValue
.get_child_optional("text");
1094 boost::optional
<boost::property_tree::ptree
&> aType
= aItemValue
.get_child_optional("type");
1095 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1096 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aItemValue
.get_child_optional("menu");
1097 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aItemValue
.get_child_optional("enabled");
1098 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aItemValue
.get_child_optional("checktype");
1099 boost::optional
<boost::property_tree::ptree
&> aChecked
= aItemValue
.get_child_optional("checked");
1101 // type is omnipresent
1102 CPPUNIT_ASSERT( aType
);
1104 // separator doesn't have any other attribs
1105 if ( aType
.get().data() == "separator" )
1107 CPPUNIT_ASSERT( !aText
&& !aCommand
&& !aSubmenu
&& !aEnabled
&& !aChecktype
&& !aChecked
);
1109 else if ( aType
.get().data() == "command" )
1111 CPPUNIT_ASSERT( aCommand
&& aText
);
1113 else if ( aType
.get().data() == "menu")
1115 CPPUNIT_ASSERT( aSubmenu
&& aText
);
1116 verifyContextMenuStructure( aSubmenu
.get() );
1121 CPPUNIT_ASSERT( aChecktype
.get().data() == "radio" ||
1122 aChecktype
.get().data() == "checkmark" ||
1123 aChecktype
.get().data() == "auto" );
1125 CPPUNIT_ASSERT( aChecked
&&
1126 ( aChecked
.get().data() == "true" || aChecked
.get().data() == "false" ) );
1132 boost::optional
<boost::property_tree::ptree
>
1133 getContextMenuItem(boost::property_tree::ptree
& aMenu
, std::string
const & unoSelector
)
1135 boost::optional
<boost::property_tree::ptree
> aMenuItem
;
1136 for (const auto& aItemPair
: aMenu
)
1138 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1140 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1141 if (aCommand
&& aCommand
.get().data() == unoSelector
)
1143 aMenuItem
= aItemValue
;
1151 } // end anonymous namespace
1153 void DesktopLOKTest::testContextMenuCalc()
1155 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
1156 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1157 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1160 Point
aPointOnImage(1150, 1100);
1161 pDocument
->pClass
->postMouseEvent(pDocument
,
1162 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1163 aPointOnImage
.X(), aPointOnImage
.Y(),
1165 Scheduler::ProcessEventsToIdle();
1167 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1168 m_aContextMenuCondition
.wait(aTimeValue
);
1170 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1171 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1172 CPPUNIT_ASSERT( aMenu
);
1173 verifyContextMenuStructure( aMenu
.get() );
1175 // tests for calc specific context menu
1178 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1179 CPPUNIT_ASSERT(aMenuItem
);
1181 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1182 CPPUNIT_ASSERT(aEnabled
);
1183 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("true"));
1188 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1189 CPPUNIT_ASSERT(aMenuItem
);
1191 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1192 CPPUNIT_ASSERT(aEnabled
);
1193 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("true"));
1198 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1199 CPPUNIT_ASSERT(aMenuItem
);
1201 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1202 CPPUNIT_ASSERT(aEnabled
);
1203 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("true"));
1206 // Remove hyperlink is disabled
1208 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:DeleteShapeHyperlink");
1209 CPPUNIT_ASSERT(aMenuItem
);
1211 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1212 CPPUNIT_ASSERT(aEnabled
);
1213 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1216 // open hyperlink is disabled
1218 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:OpenHyperlinkOnCursor");
1219 CPPUNIT_ASSERT(aMenuItem
);
1221 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1222 CPPUNIT_ASSERT(aEnabled
);
1223 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1226 // checkbutton tests
1228 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:AnchorMenu");
1229 CPPUNIT_ASSERT(aMenuItem
);
1231 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1232 CPPUNIT_ASSERT(aSubmenu
);
1234 boost::optional
<boost::property_tree::ptree
> aMenuItemToPage
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToPage");
1235 CPPUNIT_ASSERT(aMenuItemToPage
);
1237 boost::optional
<boost::property_tree::ptree
> aMenuItemToCell
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToCell");
1238 CPPUNIT_ASSERT(aMenuItemToCell
);
1240 // these are radio buttons
1241 boost::optional
<boost::property_tree::ptree
&> aChecktypeToPage
= aMenuItemToPage
.get().get_child_optional("checktype");
1242 CPPUNIT_ASSERT(aChecktypeToPage
);
1243 CPPUNIT_ASSERT_EQUAL(std::string(aChecktypeToPage
.get().data()), std::string("radio"));
1245 boost::optional
<boost::property_tree::ptree
&> aChecktypeToCell
= aMenuItemToCell
.get().get_child_optional("checktype");
1246 CPPUNIT_ASSERT(aChecktypeToCell
);
1247 CPPUNIT_ASSERT_EQUAL(std::string(aChecktypeToCell
.get().data()), std::string("radio"));
1249 // ToPage is checked
1250 boost::optional
<boost::property_tree::ptree
&> aCheckedToPage
= aMenuItemToPage
.get().get_child_optional("checked");
1251 CPPUNIT_ASSERT(aCheckedToPage
);
1252 CPPUNIT_ASSERT_EQUAL(std::string(aCheckedToPage
.get().data()), std::string("true"));
1254 // ToCell is unchecked
1255 boost::optional
<boost::property_tree::ptree
&> aCheckedToCell
= aMenuItemToCell
.get().get_child_optional("checked");
1256 CPPUNIT_ASSERT(aCheckedToCell
);
1257 CPPUNIT_ASSERT_EQUAL(std::string(aCheckedToCell
.get().data()), std::string("false"));
1261 void DesktopLOKTest::testContextMenuWriter()
1263 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1264 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1265 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1267 Point
aRandomPoint(1150, 1100);
1268 pDocument
->pClass
->postMouseEvent(pDocument
,
1269 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1270 aRandomPoint
.X(), aRandomPoint
.Y(),
1272 Scheduler::ProcessEventsToIdle();
1274 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1275 m_aContextMenuCondition
.wait(aTimeValue
);
1277 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1278 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1279 CPPUNIT_ASSERT( aMenu
);
1280 verifyContextMenuStructure( aMenu
.get() );
1282 // tests for writer specific context menu
1285 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1286 CPPUNIT_ASSERT(aMenuItem
);
1288 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1289 CPPUNIT_ASSERT(aEnabled
);
1290 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1295 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1296 CPPUNIT_ASSERT(aMenuItem
);
1298 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1299 CPPUNIT_ASSERT(aEnabled
);
1300 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1305 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1306 CPPUNIT_ASSERT(aMenuItem
);
1308 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1309 CPPUNIT_ASSERT(aEnabled
);
1310 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("true"));
1314 void DesktopLOKTest::testContextMenuImpress()
1316 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp", LOK_DOCTYPE_PRESENTATION
);
1317 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1318 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1320 // random point where we don't hit an underlying comment or text box
1321 Point
aRandomPoint(10, 1150);
1322 pDocument
->pClass
->postMouseEvent(pDocument
,
1323 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1324 aRandomPoint
.X(), aRandomPoint
.Y(),
1326 Scheduler::ProcessEventsToIdle();
1328 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1329 m_aContextMenuCondition
.wait(aTimeValue
);
1331 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1332 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1333 CPPUNIT_ASSERT( aMenu
);
1334 verifyContextMenuStructure( aMenu
.get() );
1336 // tests for impress specific context menu
1339 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1340 CPPUNIT_ASSERT(aMenuItem
);
1342 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1343 CPPUNIT_ASSERT(aEnabled
);
1344 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1349 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1350 CPPUNIT_ASSERT(aMenuItem
);
1352 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1353 CPPUNIT_ASSERT(aEnabled
);
1354 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1359 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1360 CPPUNIT_ASSERT(aMenuItem
);
1362 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1363 CPPUNIT_ASSERT(aEnabled
);
1364 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("true"));
1367 // SaveBackground is disabled
1369 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SaveBackground");
1370 CPPUNIT_ASSERT(aMenuItem
);
1372 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1373 CPPUNIT_ASSERT(aEnabled
);
1374 CPPUNIT_ASSERT_EQUAL(std::string(aEnabled
.get().data()), std::string("false"));
1377 // checkbutton tests
1379 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:ShowRuler");
1380 CPPUNIT_ASSERT(aMenuItem
);
1382 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aMenuItem
.get().get_child_optional("checktype");
1383 CPPUNIT_ASSERT(aChecktype
);
1384 CPPUNIT_ASSERT_EQUAL(std::string(aChecktype
.get().data()), std::string("checkmark"));
1386 boost::optional
<boost::property_tree::ptree
&> aChecked
= aMenuItem
.get().get_child_optional("checked");
1387 CPPUNIT_ASSERT(aChecked
);
1388 CPPUNIT_ASSERT_EQUAL(std::string(aChecked
.get().data()), std::string("false"));
1391 // Checkbutton tests inside SnapLines submenu
1393 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SnapLinesMenu");
1394 CPPUNIT_ASSERT(aMenuItem
);
1396 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1397 CPPUNIT_ASSERT(aSubmenu
);
1399 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpVis
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesVisible");
1400 CPPUNIT_ASSERT(aMenuItemHelpVis
);
1402 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpUse
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesUse");
1403 CPPUNIT_ASSERT(aMenuItemHelpUse
);
1405 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpFront
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesFront");
1406 CPPUNIT_ASSERT(aMenuItemHelpFront
);
1408 // these are checkmarks
1409 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checktype");
1410 CPPUNIT_ASSERT(aChecktypeHelpVis
);
1411 CPPUNIT_ASSERT_EQUAL(std::string(aChecktypeHelpVis
.get().data()), std::string("checkmark"));
1413 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checktype");
1414 CPPUNIT_ASSERT(aChecktypeHelpUse
);
1415 CPPUNIT_ASSERT_EQUAL(std::string(aChecktypeHelpUse
.get().data()), std::string("checkmark"));
1417 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checktype");
1418 CPPUNIT_ASSERT(aChecktypeHelpFront
);
1419 CPPUNIT_ASSERT_EQUAL(std::string(aChecktypeHelpFront
.get().data()), std::string("checkmark"));
1421 // HelplineVisible is unchecked
1422 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checked");
1423 CPPUNIT_ASSERT(aCheckedHelpVis
);
1424 CPPUNIT_ASSERT_EQUAL(std::string(aCheckedHelpVis
.get().data()), std::string("false"));
1426 // HelplineUse is checked
1427 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checked");
1428 CPPUNIT_ASSERT(aCheckedHelpUse
);
1429 CPPUNIT_ASSERT_EQUAL(std::string(aCheckedHelpUse
.get().data()), std::string("true"));
1431 // HelplineFront is checked
1432 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checked");
1433 CPPUNIT_ASSERT(aCheckedHelpFront
);
1434 CPPUNIT_ASSERT_EQUAL(std::string(aCheckedHelpFront
.get().data()), std::string("true"));
1438 static void callbackCompressionTest(const int type
, const char* payload
, void* data
)
1440 std::vector
<std::tuple
<int, std::string
>>* notifs
= static_cast<std::vector
<std::tuple
<int, std::string
>>*>(data
);
1441 notifs
->emplace_back(type
, std::string(payload
? payload
: "(nil)"));
1444 void DesktopLOKTest::testNotificationCompression()
1446 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1447 std::vector
<std::tuple
<int, std::string
>> notifs
;
1448 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1450 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""); // 0
1451 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Superseded.
1452 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""); // Should be dropped.
1453 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // 1
1454 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Should be dropped.
1455 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""); // Superseded.
1456 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""); // 2
1457 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:Bold"); // 3
1458 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""); // 4
1459 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"); // 5
1460 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // Should be dropped.
1461 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // Should be dropped.
1462 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"); // Should be dropped.
1463 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // Superseded.
1464 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // Superseded.
1465 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Superseded.
1466 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // Should be dropped.
1467 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // Should be dropped.
1468 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""); // 7
1469 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // 8
1470 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // 9
1471 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"); // 10
1472 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""); // 11
1473 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"); // Should be dropped.
1474 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"); // 12
1475 handler
->queue(LOK_CALLBACK_SET_PART
, "1"); // 13
1476 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=20"); // Superseded
1477 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""); // Should be dropped.
1478 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"); // Should be dropped.
1479 handler
->queue(LOK_CALLBACK_SET_PART
, "1"); // Should be dropped.
1480 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=1"); // 14
1482 Scheduler::ProcessEventsToIdle();
1484 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(14), notifs
.size());
1487 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
), std::get
<0>(notifs
[i
]));
1488 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1490 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1491 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1493 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1494 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1496 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1497 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), std::get
<1>(notifs
[i
++]));
1499 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1500 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1502 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_MOUSE_POINTER
), std::get
<0>(notifs
[i
]));
1503 CPPUNIT_ASSERT_EQUAL(std::string("text"), std::get
<1>(notifs
[i
++]));
1505 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION
), std::get
<0>(notifs
[i
]));
1506 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1508 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_START
), std::get
<0>(notifs
[i
]));
1509 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1511 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_END
), std::get
<0>(notifs
[i
]));
1512 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1514 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_CURSOR
), std::get
<0>(notifs
[i
]));
1515 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1517 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CURSOR_VISIBLE
), std::get
<0>(notifs
[i
]));
1518 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1520 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_FORMULA
), std::get
<0>(notifs
[i
]));
1521 CPPUNIT_ASSERT_EQUAL(std::string("blah"), std::get
<1>(notifs
[i
++]));
1523 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_SET_PART
), std::get
<0>(notifs
[i
]));
1524 CPPUNIT_ASSERT_EQUAL(std::string("1"), std::get
<1>(notifs
[i
++]));
1526 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1527 CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get
<1>(notifs
[i
++]));
1530 void DesktopLOKTest::testTileInvalidationCompression()
1532 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1534 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1535 comphelper::ScopeGuard
aGuard([]()
1537 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1540 // Single part merging
1542 std::vector
<std::tuple
<int, std::string
>> notifs
;
1543 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1545 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1546 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1547 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -50, 500, 650, 0");
1548 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0");
1549 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "100, 100, 200, 200, 0");
1551 Scheduler::ProcessEventsToIdle();
1553 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1556 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1557 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 400, 600, 0"), std::get
<1>(notifs
[i
++]));
1562 std::vector
<std::tuple
<int, std::string
>> notifs
;
1563 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1565 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1566 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1"); // Different part
1567 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, 2"); // Invalid
1568 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, 0"); // Inside first
1569 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 1"); // Invalid
1571 Scheduler::ProcessEventsToIdle();
1573 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1576 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1577 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 1"), std::get
<1>(notifs
[i
++]));
1579 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1580 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 239, 239, 0"), std::get
<1>(notifs
[i
++]));
1585 std::vector
<std::tuple
<int, std::string
>> notifs
;
1586 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1588 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0"); // 0
1589 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1"); // 1: Different part
1590 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1"); // Invalid
1591 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, -1"); // 0: All parts
1592 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1"); // Invalid
1593 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -100, 1200, 1200, -1"); // 0: All parts
1594 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 3"); // Overlapped
1595 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 2"); // 1: Unique region
1597 Scheduler::ProcessEventsToIdle();
1599 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1602 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1603 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1100, 1100, -1"), std::get
<1>(notifs
[i
++]));
1605 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1606 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 2"), std::get
<1>(notifs
[i
++]));
1609 // All Parts (partial)
1611 std::vector
<std::tuple
<int, std::string
>> notifs
;
1612 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1614 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 0"); // 0
1615 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 100, 100, 1"); // 1: Different part
1616 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1"); // Invalid
1617 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 50, 50, -1"); // 2: All-parts
1618 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1"); // Invalid
1619 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 40, 40, 3"); // Overlapped w/ 2
1620 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 4"); // 3: Unique
1621 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 1"); // 4: Unique
1623 Scheduler::ProcessEventsToIdle();
1625 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), notifs
.size());
1628 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1629 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 0"), std::get
<1>(notifs
[i
++]));
1631 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1632 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 100, 100, 1"), std::get
<1>(notifs
[i
++]));
1634 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1635 CPPUNIT_ASSERT_EQUAL(std::string("150, 150, 50, 50, -1"), std::get
<1>(notifs
[i
++]));
1637 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1638 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 4"), std::get
<1>(notifs
[i
++]));
1640 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1641 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 1"), std::get
<1>(notifs
[i
++]));
1644 // Merge with "EMPTY"
1646 std::vector
<std::tuple
<int, std::string
>> notifs
;
1647 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1649 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1650 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "EMPTY, 0");
1651 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 240, 0");
1652 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 300, 300, 0");
1653 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0");
1655 Scheduler::ProcessEventsToIdle();
1657 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1660 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1661 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1000000000, 1000000000, 0"), std::get
<1>(notifs
[i
++]));
1665 void DesktopLOKTest::testPartInInvalidation()
1667 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1668 // No part in invalidation: merge.
1670 std::vector
<std::tuple
<int, std::string
>> notifs
;
1671 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1673 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10");
1674 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10");
1676 Scheduler::ProcessEventsToIdle();
1678 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1680 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
1681 CPPUNIT_ASSERT_EQUAL(std::string("10, 10, 30, 10"), std::get
<1>(notifs
[0]));
1683 // No part in invalidation: don't merge.
1685 std::vector
<std::tuple
<int, std::string
>> notifs
;
1686 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1688 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10");
1689 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "40, 10, 20, 10");
1691 Scheduler::ProcessEventsToIdle();
1693 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1696 // Part in invalidation, intersection and parts match -> merge.
1698 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1699 comphelper::ScopeGuard
aGuard([]()
1701 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1704 std::vector
<std::tuple
<int, std::string
>> notifs
;
1705 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1707 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0");
1708 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 0");
1710 Scheduler::ProcessEventsToIdle();
1712 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1714 // Part in invalidation, intersection and parts don't match -> don't merge.
1716 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1717 comphelper::ScopeGuard
aGuard([]()
1719 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1722 std::vector
<std::tuple
<int, std::string
>> notifs
;
1723 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1725 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0");
1726 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 1");
1728 Scheduler::ProcessEventsToIdle();
1730 // This failed as RectangleAndPart::Create() always assumed no part in
1731 // payload, so this was merged -> it was 1.
1732 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1736 void DesktopLOKTest::testDialogInput()
1738 comphelper::LibreOfficeKit::setActive();
1739 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1740 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
1741 Scheduler::ProcessEventsToIdle();
1743 SfxViewShell
* pViewShell
= SfxViewShell::Current();
1744 pViewShell
->GetViewFrame()->GetBindings().Update();
1746 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
1747 CPPUNIT_ASSERT(pWindow
);
1749 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
1750 CPPUNIT_ASSERT(pCtrlFocused
);
1751 ComboBox
* pCtrlURL
= dynamic_cast<ComboBox
*>(pCtrlFocused
);
1752 CPPUNIT_ASSERT(pCtrlURL
);
1753 CPPUNIT_ASSERT_EQUAL(OUString(""), pCtrlURL
->GetText());
1755 vcl::LOKWindowId nDialogId
= pWindow
->GetLOKWindowId();
1756 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, nDialogId
, LOK_EXT_TEXTINPUT
, "wiki.");
1757 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, nDialogId
, LOK_EXT_TEXTINPUT_END
, "wiki.");
1758 pDocument
->pClass
->removeTextContext(pDocument
, nDialogId
, 1, 0);
1759 Scheduler::ProcessEventsToIdle();
1760 CPPUNIT_ASSERT_EQUAL(OUString("wiki"), pCtrlURL
->GetText());
1762 static_cast<SystemWindow
*>(pWindow
.get())->Close();
1763 Scheduler::ProcessEventsToIdle();
1766 void DesktopLOKTest::testInput()
1768 // Load a Writer document, enable change recording and press a key.
1769 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1770 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1772 Scheduler::ProcessEventsToIdle(); // Get focus & other bits setup.
1774 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "far");
1775 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "far");
1776 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
1777 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
1778 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "beyond");
1779 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "beyond");
1780 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
1781 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
1783 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "kovely");
1784 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "kovely");
1786 pDocument
->pClass
->removeTextContext(pDocument
, 0, 6, 0);
1787 // Replace it with lovely
1788 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "lovely");
1789 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "lovely");
1790 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
1791 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
1794 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
1795 Scheduler::ProcessEventsToIdle();
1796 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
1797 CPPUNIT_ASSERT(pText
!= nullptr);
1798 OString
aLovely("far beyond lovely ");
1799 CPPUNIT_ASSERT_EQUAL(aLovely
, OString(pText
));
1803 void DesktopLOKTest::testRedlineWriter()
1805 // Load a Writer document, enable change recording and press a key.
1806 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1807 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1808 xPropertySet
->setPropertyValue("RecordChanges", uno::makeAny(true));
1809 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1810 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
1811 Scheduler::ProcessEventsToIdle();
1813 // Get redline info.
1814 boost::property_tree::ptree aTree
;
1815 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
1816 std::stringstream
aStream(pJSON
);
1818 CPPUNIT_ASSERT(!aStream
.str().empty());
1819 boost::property_tree::read_json(aStream
, aTree
);
1820 // Make sure that pressing a key creates exactly one redline.
1821 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
1823 for (boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
1824 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
1825 CPPUNIT_ASSERT_EQUAL(std::string("Insert \xE2\x80\x9Ct\xE2\x80\x9D"), rRedline
.second
.get
<std::string
>("description"));
1826 // U+201C LEFT DOUBLE QUOTATION MARK, U+201D RIGHT DOUBLE QUOTATION
1830 void DesktopLOKTest::testRedlineCalc()
1832 // Load a Writer document, enable change recording and press a key.
1833 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
1834 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1835 xPropertySet
->setPropertyValue("RecordChanges", uno::makeAny(true));
1836 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1837 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
1838 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RETURN
);
1839 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RETURN
);
1840 Scheduler::ProcessEventsToIdle();
1842 // Get redline info.
1843 boost::property_tree::ptree aTree
;
1844 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
1845 std::stringstream
aStream(pJSON
);
1847 CPPUNIT_ASSERT(!aStream
.str().empty());
1848 boost::property_tree::read_json(aStream
, aTree
);
1849 // Make sure that pressing a key creates exactly one redline.
1850 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
1852 for (boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
1853 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
1854 CPPUNIT_ASSERT_EQUAL(std::string("Cell B4 changed from '5' to 't'"), rRedline
.second
.get
<std::string
>("description"));
1860 OString m_aCellFormula
;
1861 bool m_bTilesInvalidated
;
1862 tools::Rectangle m_aOwnCursor
;
1863 boost::property_tree::ptree m_aCommentCallbackResult
;
1866 : m_bTilesInvalidated(false)
1870 static void callback(int nType
, const char* pPayload
, void* pData
)
1872 static_cast<ViewCallback
*>(pData
)->callbackImpl(nType
, pPayload
);
1875 void callbackImpl(int nType
, const char* pPayload
)
1877 OString
aPayload(pPayload
);
1880 case LOK_CALLBACK_INVALIDATE_TILES
:
1882 m_bTilesInvalidated
= true;
1885 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1887 uno::Sequence
<OUString
> aSeq
= comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload
));
1888 if (OString("EMPTY") == pPayload
)
1890 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(4), aSeq
.getLength());
1891 m_aOwnCursor
.setX(aSeq
[0].toInt32());
1892 m_aOwnCursor
.setY(aSeq
[1].toInt32());
1893 m_aOwnCursor
.setWidth(aSeq
[2].toInt32());
1894 m_aOwnCursor
.setHeight(aSeq
[3].toInt32());
1897 case LOK_CALLBACK_COMMENT
:
1899 m_aCommentCallbackResult
.clear();
1900 std::stringstream
aStream(pPayload
);
1901 boost::property_tree::read_json(aStream
, m_aCommentCallbackResult
);
1902 m_aCommentCallbackResult
= m_aCommentCallbackResult
.get_child("comment");
1905 case LOK_CALLBACK_CELL_FORMULA
:
1907 m_aCellFormula
= aPayload
;
1914 void DesktopLOKTest::testPaintPartTile()
1916 // Load an impress doc of 2 slides.
1917 // ViewCallback aView1;
1918 // ViewCallback aView2;
1919 LibLODocument_Impl
* pDocument
= loadDoc("2slides.odp");
1920 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
1921 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView1);
1922 int nView1
= pDocument
->m_pDocumentClass
->getView(pDocument
);
1924 // Create a second view.
1925 pDocument
->m_pDocumentClass
->createView(pDocument
);
1926 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
1927 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView2);
1929 // Go to the second slide in the second view.
1930 pDocument
->m_pDocumentClass
->setPart(pDocument
, 1);
1932 // Switch back to the first view and start typing.
1933 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
1934 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, awt::Key::TAB
);
1935 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, awt::Key::TAB
);
1936 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
1937 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
1938 Scheduler::ProcessEventsToIdle();
1940 // Call paintPartTile() to paint the second part (in whichever view it finds suitable for this).
1941 unsigned char pPixels
[256 * 256 * 4];
1942 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, pPixels
, 1, 256, 256, 0, 0, 256, 256);
1945 Scheduler::ProcessEventsToIdle();
1946 // aView1.m_bTilesInvalidated = false;
1947 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
1948 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
1949 Scheduler::ProcessEventsToIdle();
1950 // This failed: paintPartTile() (as a side-effect) ended the text edit of
1951 // the first view, so there were no invalidations.
1952 //CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
1955 void DesktopLOKTest::testWriterCommentInsertCursor()
1957 // Load a document and type a character into the body text of the second view.
1958 ViewCallback aView1
;
1959 ViewCallback aView2
;
1960 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1961 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
1962 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView1
);
1963 pDocument
->m_pDocumentClass
->createView(pDocument
);
1964 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
1965 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView2
);
1966 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
1967 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
1968 Scheduler::ProcessEventsToIdle();
1969 tools::Rectangle aBodyCursor
= aView2
.m_aOwnCursor
;
1971 // Now insert a comment and make sure that the comment's cursor is shown,
1972 // not the body text's one.
1973 aView1
.m_aOwnCursor
.SetEmpty();
1974 const int nCtrlAltC
= KEY_MOD1
+ KEY_MOD2
+ 512 + 'c' - 'a';
1975 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'c', nCtrlAltC
);
1976 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'c', nCtrlAltC
);
1977 Scheduler::ProcessEventsToIdle();
1978 // Wait for SfxBindings to actually update the state, which updated the
1980 osl::Thread::wait(std::chrono::seconds(1));
1981 Scheduler::ProcessEventsToIdle();
1982 // This failed: the body cursor was shown right after inserting a comment.
1983 CPPUNIT_ASSERT(aView2
.m_aOwnCursor
.getX() > aBodyCursor
.getX());
1984 // This failed, the first view's cursor also jumped when the second view
1985 // inserted the comment.
1986 CPPUNIT_ASSERT(aView1
.m_aOwnCursor
.IsEmpty());
1988 Scheduler::ProcessEventsToIdle();
1989 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, nullptr, reinterpret_cast<void*>(1));
1993 void DesktopLOKTest::testGetFontSubset()
1995 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1996 OUString aFontName
= rtl::Uri::encode(
1997 OUString("Liberation Sans"),
1998 rtl_UriCharClassRelSegment
,
1999 rtl_UriEncodeKeepEscapes
,
2000 RTL_TEXTENCODING_UTF8
2002 OString aCommand
= OUStringToOString(".uno:FontSubset&name=" + aFontName
, RTL_TEXTENCODING_UTF8
);
2003 boost::property_tree::ptree aTree
;
2004 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aCommand
.getStr());
2005 std::stringstream
aStream(pJSON
);
2006 boost::property_tree::read_json(aStream
, aTree
);
2007 CPPUNIT_ASSERT( !aTree
.empty() );
2008 CPPUNIT_ASSERT_EQUAL( std::string(".uno:FontSubset"), aTree
.get_child("commandName").get_value
<std::string
>() );
2009 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
2010 CPPUNIT_ASSERT( !aValues
.empty() );
2015 void DesktopLOKTest::testCommentsWriter()
2017 // Disable tiled rendering for comments
2018 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2020 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2021 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2022 long nWidth
, nHeight
;
2023 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
2025 // Document width alongwith without sidebar comes to be < 13000
2026 CPPUNIT_ASSERT( nWidth
< 13000 );
2028 // Can we get all the comments using .uno:ViewAnnotations command ?
2029 boost::property_tree::ptree aTree
;
2030 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2031 std::stringstream
aStream(pJSON
);
2033 CPPUNIT_ASSERT(!aStream
.str().empty());
2034 boost::property_tree::read_json(aStream
, aTree
);
2035 // There are 3 comments in the document already
2036 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aTree
.get_child("comments").size());
2038 int nComment2Id
= 0;
2039 // Check if all comment fields have valid data
2040 for (const auto& rComment
: aTree
.get_child("comments"))
2042 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2043 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("author").empty());
2044 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("text").empty());
2045 // Has a valid iso 8601 date time string
2046 css::util::DateTime aDateTime
;
2047 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2048 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2050 // This comment has a marked text range
2051 if (rComment
.second
.get
<std::string
>("text") == "Comment 2")
2053 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("textRange").empty());
2054 nComment2Id
= rComment
.second
.get
<int>("id");
2056 // This is a reply comment
2057 else if (rComment
.second
.get
<std::string
>("text") == "Reply to Comment 2")
2059 CPPUNIT_ASSERT_EQUAL(nComment2Id
, rComment
.second
.get
<int>("parent"));
2063 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2067 void DesktopLOKTest::testCommentsCalc()
2069 // Disable tiled rendering for comments
2070 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2072 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2073 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2075 // Can we get all the comments using .uno:ViewAnnotations command ?
2076 boost::property_tree::ptree aTree
;
2077 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2078 std::stringstream
aStream(pJSON
);
2080 CPPUNIT_ASSERT(!aStream
.str().empty());
2081 boost::property_tree::read_json(aStream
, aTree
);
2082 // There are 2 comments in the document already
2083 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2085 // Check if all comment fields have valid data
2087 for (const auto& rComment
: aTree
.get_child("comments"))
2093 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2094 CPPUNIT_ASSERT_EQUAL(std::string("Comment1"), rComment
.second
.get
<std::string
>("text"));
2095 CPPUNIT_ASSERT_EQUAL(std::string("7650, 3570, 1274, 254"), rComment
.second
.get
<std::string
>("cellPos"));
2100 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2101 CPPUNIT_ASSERT_EQUAL(std::string("Comment2"), rComment
.second
.get
<std::string
>("text"));
2102 CPPUNIT_ASSERT_EQUAL(std::string("8925, 4335, 1274, 254"), rComment
.second
.get
<std::string
>("cellPos"));
2110 // We checked all the comments
2111 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2113 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2117 void DesktopLOKTest::testCommentsImpress()
2119 // Disable tiled rendering for comments
2120 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2122 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
2123 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2125 // Can we get all the comments using .uno:ViewAnnotations command ?
2126 boost::property_tree::ptree aTree
;
2127 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2128 std::stringstream
aStream(pJSON
);
2130 CPPUNIT_ASSERT(!aStream
.str().empty());
2131 boost::property_tree::read_json(aStream
, aTree
);
2132 // There are 2 comments in the document already
2133 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2135 // Check if all comment fields have valid data
2137 for (const auto& rComment
: aTree
.get_child("comments"))
2143 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2144 CPPUNIT_ASSERT_EQUAL(std::string("This is comment1"), rComment
.second
.get
<std::string
>("text"));
2145 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), rComment
.second
.get
<std::string
>("author"));
2146 css::util::DateTime aDateTime
;
2147 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2148 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2153 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2154 CPPUNIT_ASSERT_EQUAL(std::string("This is comment2"), rComment
.second
.get
<std::string
>("text"));
2155 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), rComment
.second
.get
<std::string
>("author"));
2156 css::util::DateTime aDateTime
;
2157 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2158 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2166 // We checked all the comments
2167 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2169 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2172 void DesktopLOKTest::testCommentsCallbacksWriter()
2174 // Comments callback are emitted only if tiled annotations are off
2175 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2176 ViewCallback aView1
;
2177 ViewCallback aView2
;
2178 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2179 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2180 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView1
);
2181 pDocument
->m_pDocumentClass
->createView(pDocument
);
2182 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2183 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView2
);
2185 // Add a new comment
2186 OString
aCommandArgs("{ \"Text\": { \"type\": \"string\", \"value\": \"Additional comment\" }, \"Author\": { \"type\": \"string\", \"value\": \"LOK User1\" } }");
2187 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2188 Scheduler::ProcessEventsToIdle();
2190 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2191 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2192 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2193 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2195 // Reply to a comment just added
2196 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment\" } }";
2197 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2198 Scheduler::ProcessEventsToIdle();
2200 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2201 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2202 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2203 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2204 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2205 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2206 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2207 int nCommentId2
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2209 // Edit the previously added comment
2210 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Edited comment\" } }";
2211 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2212 Scheduler::ProcessEventsToIdle();
2214 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2215 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2216 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2217 // parent is unchanged still
2218 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2219 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2220 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2221 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2223 // Delete the reply comment just added
2224 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" } }";
2225 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteComment", aCommandArgs
.getStr(), false);
2226 Scheduler::ProcessEventsToIdle();
2228 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2229 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2230 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2231 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2232 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView2
.m_aCommentCallbackResult
.get
<int>("id"));
2234 // Reply to nCommentId1 again
2235 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment again\" } }";
2236 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2237 Scheduler::ProcessEventsToIdle();
2239 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2240 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2241 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2242 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2243 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2244 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2245 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2247 // .uno:ViewAnnotations returns total of 5 comments
2248 boost::property_tree::ptree aTree
;
2249 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2250 std::stringstream
aStream(pJSON
);
2252 CPPUNIT_ASSERT(!aStream
.str().empty());
2253 boost::property_tree::read_json(aStream
, aTree
);
2254 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aTree
.get_child("comments").size());
2256 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, nullptr, reinterpret_cast<void*>(1));
2259 void DesktopLOKTest::testRunMacro()
2261 LibLibreOffice_Impl aOffice
;
2262 bool bGoodMacro
, bNonExistentMacro
;
2264 // Tools macros come pre-installed in system share/basic folder,
2265 bGoodMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, OString("macro:///Tools.Debug.ActivateReadOnlyFlag()").getStr());
2266 CPPUNIT_ASSERT(bGoodMacro
);
2268 bNonExistentMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, OString("macro:///I.Am.Not(There)").getStr());
2269 CPPUNIT_ASSERT(!bNonExistentMacro
);
2272 void DesktopLOKTest::testExtractParameter()
2274 OUString
aOptions("Language=de-DE");
2275 OUString aValue
= extractParameter(aOptions
, "Language");
2276 CPPUNIT_ASSERT_EQUAL(OUString("de-DE"), aValue
);
2277 CPPUNIT_ASSERT_EQUAL(OUString(), aOptions
);
2279 aOptions
= "Language=en-US,Something";
2280 aValue
= extractParameter(aOptions
, "Language");
2281 CPPUNIT_ASSERT_EQUAL(OUString("en-US"), aValue
);
2282 CPPUNIT_ASSERT_EQUAL(OUString("Something"), aOptions
);
2284 aOptions
= "SomethingElse,Language=cs-CZ";
2285 aValue
= extractParameter(aOptions
, "Language");
2286 CPPUNIT_ASSERT_EQUAL(OUString("cs-CZ"), aValue
);
2287 CPPUNIT_ASSERT_EQUAL(OUString("SomethingElse"), aOptions
);
2289 aOptions
= "Something1,Language=hu-HU,Something2";
2290 aValue
= extractParameter(aOptions
, "Language");
2291 CPPUNIT_ASSERT_EQUAL(OUString("hu-HU"), aValue
);
2292 CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2"), aOptions
);
2294 aOptions
= "Something1,Something2=blah,Something3";
2295 aValue
= extractParameter(aOptions
, "Language");
2296 CPPUNIT_ASSERT_EQUAL(OUString(), aValue
);
2297 CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2=blah,Something3"), aOptions
);
2300 void DesktopLOKTest::readFileIntoByteVector(OUString
const & sFilename
, std::vector
<unsigned char> & rByteVector
)
2302 rByteVector
.clear();
2304 createFileURL(sFilename
, aURL
);
2305 SvFileStream
aStream(aURL
, StreamMode::READ
);
2306 rByteVector
.resize(aStream
.remainingSize());
2307 aStream
.ReadBytes(rByteVector
.data(), aStream
.remainingSize());
2310 void DesktopLOKTest::testGetSignatureState_Signed()
2312 LibLODocument_Impl
* pDocument
= loadDoc("signed.odt");
2313 Scheduler::ProcessEventsToIdle();
2314 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2315 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2316 CPPUNIT_ASSERT_EQUAL(int(4), nState
);
2318 std::vector
<unsigned char> aCertificate
;
2320 readFileIntoByteVector("rootCA.der", aCertificate
);
2321 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2322 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2323 CPPUNIT_ASSERT(bResult
);
2327 readFileIntoByteVector("intermediateRootCA.der", aCertificate
);
2328 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2329 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2330 CPPUNIT_ASSERT(bResult
);
2333 nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2334 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2337 void DesktopLOKTest::testGetSignatureState_NonSigned()
2339 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2340 Scheduler::ProcessEventsToIdle();
2341 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2342 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2343 CPPUNIT_ASSERT_EQUAL(int(0), nState
);
2346 void DesktopLOKTest::testInsertCertificate_DER_ODT()
2348 // Load the document, save it into a temp file and load that file again
2349 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2350 utl::TempFile aTempFile
;
2351 aTempFile
.EnableKillingFile();
2352 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2355 mxComponent
= loadFromDesktop(aTempFile
.GetURL(), "com.sun.star.text.TextDocument");
2356 pDocument
= new LibLODocument_Impl(mxComponent
);
2358 Scheduler::ProcessEventsToIdle();
2359 CPPUNIT_ASSERT(mxComponent
.is());
2360 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2361 Scheduler::ProcessEventsToIdle();
2363 std::vector
<unsigned char> aCertificate
;
2364 std::vector
<unsigned char> aPrivateKey
;
2367 readFileIntoByteVector("rootCA.der", aCertificate
);
2369 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2370 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2371 CPPUNIT_ASSERT(bResult
);
2375 readFileIntoByteVector("intermediateRootCA.der", aCertificate
);
2377 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2378 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2379 CPPUNIT_ASSERT(bResult
);
2383 readFileIntoByteVector("certificate.der", aCertificate
);
2384 readFileIntoByteVector("certificatePrivateKey.der", aPrivateKey
);
2386 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2387 aCertificate
.data(), int(aCertificate
.size()),
2388 aPrivateKey
.data(), int(aPrivateKey
.size()));
2389 CPPUNIT_ASSERT(bResult
);
2392 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2393 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2397 void DesktopLOKTest::testInsertCertificate_PEM_ODT()
2399 // Load the document, save it into a temp file and load that file again
2400 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2401 utl::TempFile aTempFile
;
2402 aTempFile
.EnableKillingFile();
2403 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2406 mxComponent
= loadFromDesktop(aTempFile
.GetURL(), "com.sun.star.text.TextDocument");
2407 pDocument
= new LibLODocument_Impl(mxComponent
);
2409 Scheduler::ProcessEventsToIdle();
2410 CPPUNIT_ASSERT(mxComponent
.is());
2411 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2412 Scheduler::ProcessEventsToIdle();
2414 std::vector
<unsigned char> aCertificate
;
2415 std::vector
<unsigned char> aPrivateKey
;
2418 readFileIntoByteVector("test-cert-chain-1.pem", aCertificate
);
2420 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2421 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2422 CPPUNIT_ASSERT(bResult
);
2426 readFileIntoByteVector("test-cert-chain-2.pem", aCertificate
);
2428 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2429 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2430 CPPUNIT_ASSERT(bResult
);
2434 readFileIntoByteVector("test-cert-chain-3.pem", aCertificate
);
2436 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2437 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2438 CPPUNIT_ASSERT(bResult
);
2442 readFileIntoByteVector("test-cert-signing.pem", aCertificate
);
2443 readFileIntoByteVector("test-PK-signing.pem", aPrivateKey
);
2445 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2446 aCertificate
.data(), int(aCertificate
.size()),
2447 aPrivateKey
.data(), int(aPrivateKey
.size()));
2448 CPPUNIT_ASSERT(bResult
);
2451 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2452 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2455 void DesktopLOKTest::testInsertCertificate_PEM_DOCX()
2457 // Load the document, save it into a temp file and load that file again
2458 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.docx");
2459 utl::TempFile aTempFile
;
2460 aTempFile
.EnableKillingFile();
2461 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "docx", nullptr));
2464 mxComponent
= loadFromDesktop(aTempFile
.GetURL(), "com.sun.star.text.TextDocument");
2465 pDocument
= new LibLODocument_Impl(mxComponent
);
2467 Scheduler::ProcessEventsToIdle();
2468 CPPUNIT_ASSERT(mxComponent
.is());
2469 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2470 Scheduler::ProcessEventsToIdle();
2472 std::vector
<unsigned char> aCertificate
;
2473 std::vector
<unsigned char> aPrivateKey
;
2476 readFileIntoByteVector("test-cert-chain-1.pem", aCertificate
);
2478 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2479 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2480 CPPUNIT_ASSERT(bResult
);
2484 readFileIntoByteVector("test-cert-chain-2.pem", aCertificate
);
2486 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2487 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2488 CPPUNIT_ASSERT(bResult
);
2492 readFileIntoByteVector("test-cert-chain-3.pem", aCertificate
);
2494 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2495 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2496 CPPUNIT_ASSERT(bResult
);
2500 readFileIntoByteVector("test-cert-signing.pem", aCertificate
);
2501 readFileIntoByteVector("test-PK-signing.pem", aPrivateKey
);
2503 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2504 aCertificate
.data(), int(aCertificate
.size()),
2505 aPrivateKey
.data(), int(aPrivateKey
.size()));
2506 CPPUNIT_ASSERT(bResult
);
2509 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2510 CPPUNIT_ASSERT_EQUAL(int(5), nState
);
2513 void DesktopLOKTest::testSignDocument_PEM_PDF()
2515 // FIXME: needs investigation ...
2518 // Load the document, save it into a temp file and load that file again
2519 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2520 utl::TempFile aTempFile
;
2521 aTempFile
.EnableKillingFile();
2523 Scheduler::ProcessEventsToIdle();
2524 CPPUNIT_ASSERT(mxComponent
.is());
2525 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2526 Scheduler::ProcessEventsToIdle();
2528 std::vector
<unsigned char> aCertificate
;
2529 std::vector
<unsigned char> aPrivateKey
;
2532 readFileIntoByteVector("test-cert-chain-1.pem", aCertificate
);
2534 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2535 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2536 CPPUNIT_ASSERT(bResult
);
2540 readFileIntoByteVector("test-cert-chain-2.pem", aCertificate
);
2542 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2543 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2544 CPPUNIT_ASSERT(bResult
);
2548 readFileIntoByteVector("test-cert-chain-3.pem", aCertificate
);
2550 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2551 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2552 CPPUNIT_ASSERT(bResult
);
2555 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "pdf", nullptr));
2559 Scheduler::ProcessEventsToIdle();
2561 readFileIntoByteVector("test-cert-signing.pem", aCertificate
);
2562 readFileIntoByteVector("test-PK-signing.pem", aPrivateKey
);
2564 LibLibreOffice_Impl aOffice
;
2565 bool bResult
= aOffice
.m_pOfficeClass
->signDocument(&aOffice
, aTempFile
.GetURL().toUtf8().getStr(),
2566 aCertificate
.data(), int(aCertificate
.size()),
2567 aPrivateKey
.data(), int(aPrivateKey
.size()));
2569 CPPUNIT_ASSERT(bResult
);
2572 void DesktopLOKTest::testTextSelectionHandles()
2574 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2575 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
2577 OString
aText("hello");
2578 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
2580 // select the inserted text
2581 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2582 Scheduler::ProcessEventsToIdle();
2583 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2584 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2586 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart
);
2587 CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd
);
2590 m_aTextSelectionStart
= "";
2591 m_aTextSelectionEnd
= "";
2592 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
2593 Scheduler::ProcessEventsToIdle();
2594 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2595 CPPUNIT_ASSERT_EQUAL(OString(), OString(pText
));
2597 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionStart
);
2598 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionEnd
);
2600 // select again; the positions of the selection handles have to be sent
2602 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2603 Scheduler::ProcessEventsToIdle();
2604 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2605 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2607 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart
);
2608 CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd
);
2611 void DesktopLOKTest::testDialogPaste()
2613 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2614 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
2615 Scheduler::ProcessEventsToIdle();
2617 SfxViewShell
* pViewShell
= SfxViewShell::Current();
2618 pViewShell
->GetViewFrame()->GetBindings().Update();
2620 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
2621 CPPUNIT_ASSERT(pWindow
);
2623 pDocument
->pClass
->postWindow(pDocument
, pWindow
->GetLOKWindowId(), LOK_WINDOW_PASTE
,
2624 "{ \"MimeType\" : { \"type\" : \"string\", \"value\" : \"text/plain;charset=utf-8\" }, \"Data\" : { \"type\" : \"[]byte\", \"value\" : \"www.softwarelibre.org.bo\" } }");
2625 Scheduler::ProcessEventsToIdle();
2627 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
2628 CPPUNIT_ASSERT(pCtrlFocused
);
2629 ComboBox
* pCtrlURL
= dynamic_cast<ComboBox
*>(pCtrlFocused
);
2630 CPPUNIT_ASSERT(pCtrlURL
);
2631 CPPUNIT_ASSERT_EQUAL(OUString("www.softwarelibre.org.bo"), pCtrlURL
->GetText());
2633 static_cast<SystemWindow
*>(pWindow
.get())->Close();
2634 Scheduler::ProcessEventsToIdle();
2637 void DesktopLOKTest::testComplexSelection()
2639 // Start with a blank text file and add contents.
2640 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2641 // LibLODocument_Impl* pDocument = loadDoc("sheet_with_image.ods");
2642 static const OString
aText("hello world");
2644 // Certainly not complex.
2645 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
2648 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
2651 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
2655 createFileURL("paste.jpg", aFileURL
);
2656 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
2657 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
2658 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
2661 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2662 Scheduler::ProcessEventsToIdle();
2664 // We expect this to be complex.
2665 const int type
= pDocument
->pClass
->getSelectionType(pDocument
);
2666 CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_COMPLEX
, type
);
2668 // Export as plain text, we should get only the text part "hello".
2669 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2670 CPPUNIT_ASSERT(pText
!= nullptr);
2671 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2674 // Export as rtf, we should also get the image.
2675 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/rtf", nullptr);
2676 CPPUNIT_ASSERT(pText
!= nullptr);
2677 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
2678 CPPUNIT_ASSERT(std::string(pText
).find("pict{") != std::string::npos
); // Must have the image as well.
2681 // Export as html, we should also get the image.
2682 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/html", nullptr);
2683 CPPUNIT_ASSERT(pText
!= nullptr);
2684 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
2685 CPPUNIT_ASSERT(std::string(pText
).find("<img") != std::string::npos
); // Must have the image as well.
2688 // We expect this to be complex.
2689 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_COMPLEX
), pDocument
->pClass
->getSelectionType(pDocument
));
2692 void DesktopLOKTest::testCalcSaveAs()
2694 comphelper::LibreOfficeKit::setActive();
2696 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2697 CPPUNIT_ASSERT(pDocument
);
2699 // Enter some text, but don't commit.
2700 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'X', 0);
2701 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'X', 0);
2702 Scheduler::ProcessEventsToIdle();
2704 // Save as a new file.
2705 OUString aNewFileUrl
= "file:///tmp/saveas.ods";
2706 pDocument
->pClass
->saveAs(pDocument
, aNewFileUrl
.toUtf8().getStr(), nullptr, nullptr);
2709 // Load the new document and verify that the in-flight changes are saved.
2710 pDocument
= loadDocUrl(aNewFileUrl
, LOK_DOCTYPE_SPREADSHEET
);
2711 CPPUNIT_ASSERT(pDocument
);
2714 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2715 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView
);
2717 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RIGHT
);
2718 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RIGHT
);
2719 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_LEFT
);
2720 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_LEFT
);
2721 Scheduler::ProcessEventsToIdle();
2723 CPPUNIT_ASSERT_EQUAL(OString("X"), aView
.m_aCellFormula
);
2726 void DesktopLOKTest::testSpellcheckerMultiView()
2728 static const OUString
aLangISO("en-US");
2729 SvtSysLocaleOptions aSysLocaleOptions
;
2730 aSysLocaleOptions
.SetLocaleConfigString(aLangISO
);
2731 aSysLocaleOptions
.SetUILocaleConfigString(aLangISO
);
2732 comphelper::LibreOfficeKit::setLanguageTag(aLangISO
, true);
2734 auto aSavedSettings
= Application::GetSettings();
2735 std::unique_ptr
<Resetter
> pResetter(
2736 new Resetter([&]() { Application::SetSettings(aSavedSettings
); }));
2737 AllSettings
aSettings(aSavedSettings
);
2738 aSettings
.SetLanguageTag(aLangISO
, true);
2739 Application::SetSettings(aSettings
);
2741 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
2742 pDocument
->pClass
->setViewLanguage(pDocument
, 0, "en-US"); // For spellchecking.
2743 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
2744 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
2746 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2747 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2748 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2749 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
2751 // Start spellchecking.
2752 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SpellDialog", nullptr, false);
2754 // Uncommenting this will result in a deadlock.
2755 // Because the language configuration above is not effective, and no
2756 // language is actually set, the spell-dialog finds no misspelled
2757 // words, and displays a message box, which must be dismissed to
2759 // Need to fix the language configuration issue to enable this.
2760 // Scheduler::ProcessEventsToIdle();
2762 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2764 // Now create another view.
2765 const int nViewId
= pDocument
->m_pDocumentClass
->createView(pDocument
);
2766 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2769 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nViewId
);
2771 // We should survive the destroyed view.
2772 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2777 constexpr size_t classOffset(int i
)
2779 return sizeof(static_cast<struct _LibreOfficeKitClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
2782 constexpr size_t documentClassOffset(int i
)
2784 return sizeof(static_cast<struct _LibreOfficeKitDocumentClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
2789 void DesktopLOKTest::testABI()
2791 // STABLE ABI, NEVER CHANGE (unless there's a very good reason, agreed by ESC, etc.)
2792 CPPUNIT_ASSERT_EQUAL(classOffset(0), offsetof(struct _LibreOfficeKitClass
, destroy
));
2793 CPPUNIT_ASSERT_EQUAL(classOffset(1), offsetof(struct _LibreOfficeKitClass
, documentLoad
));
2794 CPPUNIT_ASSERT_EQUAL(classOffset(2), offsetof(struct _LibreOfficeKitClass
, getError
));
2795 CPPUNIT_ASSERT_EQUAL(classOffset(3), offsetof(struct _LibreOfficeKitClass
, documentLoadWithOptions
));
2796 CPPUNIT_ASSERT_EQUAL(classOffset(4), offsetof(struct _LibreOfficeKitClass
, freeError
));
2797 CPPUNIT_ASSERT_EQUAL(classOffset(5), offsetof(struct _LibreOfficeKitClass
, registerCallback
));
2798 CPPUNIT_ASSERT_EQUAL(classOffset(6), offsetof(struct _LibreOfficeKitClass
, getFilterTypes
));
2799 CPPUNIT_ASSERT_EQUAL(classOffset(7), offsetof(struct _LibreOfficeKitClass
, setOptionalFeatures
));
2800 CPPUNIT_ASSERT_EQUAL(classOffset(8), offsetof(struct _LibreOfficeKitClass
, setDocumentPassword
));
2801 CPPUNIT_ASSERT_EQUAL(classOffset(9), offsetof(struct _LibreOfficeKitClass
, getVersionInfo
));
2802 CPPUNIT_ASSERT_EQUAL(classOffset(10), offsetof(struct _LibreOfficeKitClass
, runMacro
));
2803 CPPUNIT_ASSERT_EQUAL(classOffset(11), offsetof(struct _LibreOfficeKitClass
, signDocument
));
2805 CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass
, destroy
));
2806 CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass
, saveAs
));
2808 // Unstable ABI, but still think twice before changing this
2809 // Eg. can't you add your new member at the end of the struct instead of
2810 // in the middle? The thing you are changing - is it already part of some
2812 CPPUNIT_ASSERT_EQUAL(documentClassOffset(2), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentType
));
2813 CPPUNIT_ASSERT_EQUAL(documentClassOffset(3), offsetof(struct _LibreOfficeKitDocumentClass
, getParts
));
2814 CPPUNIT_ASSERT_EQUAL(documentClassOffset(4), offsetof(struct _LibreOfficeKitDocumentClass
, getPartPageRectangles
));
2815 CPPUNIT_ASSERT_EQUAL(documentClassOffset(5), offsetof(struct _LibreOfficeKitDocumentClass
, getPart
));
2816 CPPUNIT_ASSERT_EQUAL(documentClassOffset(6), offsetof(struct _LibreOfficeKitDocumentClass
, setPart
));
2817 CPPUNIT_ASSERT_EQUAL(documentClassOffset(7), offsetof(struct _LibreOfficeKitDocumentClass
, getPartName
));
2818 CPPUNIT_ASSERT_EQUAL(documentClassOffset(8), offsetof(struct _LibreOfficeKitDocumentClass
, setPartMode
));
2819 CPPUNIT_ASSERT_EQUAL(documentClassOffset(9), offsetof(struct _LibreOfficeKitDocumentClass
, paintTile
));
2820 CPPUNIT_ASSERT_EQUAL(documentClassOffset(10), offsetof(struct _LibreOfficeKitDocumentClass
, getTileMode
));
2821 CPPUNIT_ASSERT_EQUAL(documentClassOffset(11), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentSize
));
2822 CPPUNIT_ASSERT_EQUAL(documentClassOffset(12), offsetof(struct _LibreOfficeKitDocumentClass
, initializeForRendering
));
2823 CPPUNIT_ASSERT_EQUAL(documentClassOffset(13), offsetof(struct _LibreOfficeKitDocumentClass
, registerCallback
));
2824 CPPUNIT_ASSERT_EQUAL(documentClassOffset(14), offsetof(struct _LibreOfficeKitDocumentClass
, postKeyEvent
));
2825 CPPUNIT_ASSERT_EQUAL(documentClassOffset(15), offsetof(struct _LibreOfficeKitDocumentClass
, postMouseEvent
));
2826 CPPUNIT_ASSERT_EQUAL(documentClassOffset(16), offsetof(struct _LibreOfficeKitDocumentClass
, postUnoCommand
));
2827 CPPUNIT_ASSERT_EQUAL(documentClassOffset(17), offsetof(struct _LibreOfficeKitDocumentClass
, setTextSelection
));
2828 CPPUNIT_ASSERT_EQUAL(documentClassOffset(18), offsetof(struct _LibreOfficeKitDocumentClass
, getTextSelection
));
2829 CPPUNIT_ASSERT_EQUAL(documentClassOffset(19), offsetof(struct _LibreOfficeKitDocumentClass
, paste
));
2830 CPPUNIT_ASSERT_EQUAL(documentClassOffset(20), offsetof(struct _LibreOfficeKitDocumentClass
, setGraphicSelection
));
2831 CPPUNIT_ASSERT_EQUAL(documentClassOffset(21), offsetof(struct _LibreOfficeKitDocumentClass
, resetSelection
));
2832 CPPUNIT_ASSERT_EQUAL(documentClassOffset(22), offsetof(struct _LibreOfficeKitDocumentClass
, getCommandValues
));
2833 CPPUNIT_ASSERT_EQUAL(documentClassOffset(23), offsetof(struct _LibreOfficeKitDocumentClass
, setClientZoom
));
2834 CPPUNIT_ASSERT_EQUAL(documentClassOffset(24), offsetof(struct _LibreOfficeKitDocumentClass
, setClientVisibleArea
));
2835 CPPUNIT_ASSERT_EQUAL(documentClassOffset(25), offsetof(struct _LibreOfficeKitDocumentClass
, createView
));
2836 CPPUNIT_ASSERT_EQUAL(documentClassOffset(26), offsetof(struct _LibreOfficeKitDocumentClass
, destroyView
));
2837 CPPUNIT_ASSERT_EQUAL(documentClassOffset(27), offsetof(struct _LibreOfficeKitDocumentClass
, setView
));
2838 CPPUNIT_ASSERT_EQUAL(documentClassOffset(28), offsetof(struct _LibreOfficeKitDocumentClass
, getView
));
2839 CPPUNIT_ASSERT_EQUAL(documentClassOffset(29), offsetof(struct _LibreOfficeKitDocumentClass
, getViewsCount
));
2840 CPPUNIT_ASSERT_EQUAL(documentClassOffset(30), offsetof(struct _LibreOfficeKitDocumentClass
, renderFont
));
2841 CPPUNIT_ASSERT_EQUAL(documentClassOffset(31), offsetof(struct _LibreOfficeKitDocumentClass
, getPartHash
));
2842 CPPUNIT_ASSERT_EQUAL(documentClassOffset(32), offsetof(struct _LibreOfficeKitDocumentClass
, paintPartTile
));
2843 CPPUNIT_ASSERT_EQUAL(documentClassOffset(33), offsetof(struct _LibreOfficeKitDocumentClass
, getViewIds
));
2844 CPPUNIT_ASSERT_EQUAL(documentClassOffset(34), offsetof(struct _LibreOfficeKitDocumentClass
, setOutlineState
));
2845 CPPUNIT_ASSERT_EQUAL(documentClassOffset(35), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindow
));
2846 CPPUNIT_ASSERT_EQUAL(documentClassOffset(36), offsetof(struct _LibreOfficeKitDocumentClass
, postWindow
));
2847 CPPUNIT_ASSERT_EQUAL(documentClassOffset(37), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowKeyEvent
));
2848 CPPUNIT_ASSERT_EQUAL(documentClassOffset(38), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowMouseEvent
));
2849 CPPUNIT_ASSERT_EQUAL(documentClassOffset(39), offsetof(struct _LibreOfficeKitDocumentClass
, setViewLanguage
));
2850 CPPUNIT_ASSERT_EQUAL(documentClassOffset(40), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowExtTextInputEvent
));
2851 CPPUNIT_ASSERT_EQUAL(documentClassOffset(41), offsetof(struct _LibreOfficeKitDocumentClass
, getPartInfo
));
2852 CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowDPI
));
2853 CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass
, insertCertificate
));
2854 CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), offsetof(struct _LibreOfficeKitDocumentClass
, addCertificate
));
2855 CPPUNIT_ASSERT_EQUAL(documentClassOffset(45), offsetof(struct _LibreOfficeKitDocumentClass
, getSignatureState
));
2856 CPPUNIT_ASSERT_EQUAL(documentClassOffset(46), offsetof(struct _LibreOfficeKitDocumentClass
, renderShapeSelection
));
2857 CPPUNIT_ASSERT_EQUAL(documentClassOffset(47), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowGestureEvent
));
2858 CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass
, createViewWithOptions
));
2859 CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass
, selectPart
));
2860 CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass
, moveSelectedParts
));
2861 CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass
, resizeWindow
));
2862 CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass
, getClipboard
));
2863 CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass
, setClipboard
));
2864 CPPUNIT_ASSERT_EQUAL(documentClassOffset(54), offsetof(struct _LibreOfficeKitDocumentClass
, getSelectionType
));
2865 CPPUNIT_ASSERT_EQUAL(documentClassOffset(55), offsetof(struct _LibreOfficeKitDocumentClass
, removeTextContext
));
2866 CPPUNIT_ASSERT_EQUAL(documentClassOffset(56), offsetof(struct _LibreOfficeKitDocumentClass
, sendDialogEvent
));
2867 CPPUNIT_ASSERT_EQUAL(documentClassOffset(57), offsetof(struct _LibreOfficeKitDocumentClass
, renderFontOrientation
));
2868 CPPUNIT_ASSERT_EQUAL(documentClassOffset(58), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowForView
));
2869 CPPUNIT_ASSERT_EQUAL(documentClassOffset(59), offsetof(struct _LibreOfficeKitDocumentClass
, completeFunction
));
2871 // Extending is fine, update this, and add new assert for the offsetof the
2873 CPPUNIT_ASSERT_EQUAL(documentClassOffset(60), sizeof(struct _LibreOfficeKitDocumentClass
));
2876 CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest
);
2878 CPPUNIT_PLUGIN_IMPLEMENT();
2880 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */