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/.
10 #include <config_oox.h>
12 #include <string_view>
14 #include <com/sun/star/lang/XComponent.hpp>
15 #include <com/sun/star/text/XTextDocument.hpp>
16 #include <com/sun/star/awt/Key.hpp>
17 #include <com/sun/star/awt/XReschedule.hpp>
18 #include <com/sun/star/awt/Toolkit.hpp>
19 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
20 #include <com/sun/star/text/TextContentAnchorType.hpp>
21 #include <boost/property_tree/json_parser.hpp>
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/util/XCloseable.hpp>
25 #include <vcl/scheduler.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/syswin.hxx>
28 #include <vcl/window.hxx>
29 #include <vcl/ctrl.hxx>
30 #include <vcl/uitest/uiobject.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <rtl/math.hxx>
33 #include <sfx2/childwin.hxx>
34 #include <sfx2/lokhelper.hxx>
35 #include <test/unoapi_test.hxx>
36 #include <comphelper/lok.hxx>
37 #include <comphelper/propertysequence.hxx>
38 #include <osl/conditn.hxx>
39 #include <svl/srchitem.hxx>
40 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
41 #include <unotools/tempfile.hxx>
42 #include <sfx2/viewsh.hxx>
43 #include <sfx2/viewfrm.hxx>
44 #include <sfx2/bindings.hxx>
45 #include <unotools/datetime.hxx>
46 #include <unotools/syslocaleoptions.hxx>
47 #include <comphelper/string.hxx>
48 #include <comphelper/scopeguard.hxx>
50 #include <config_fonts.h>
51 #include <config_mpl.h>
52 #include <tools/json_writer.hxx>
53 #include <o3tl/unit_conversion.hxx>
54 #include <o3tl/string_view.hxx>
56 #include <lib/init.hxx>
57 #include <svx/svxids.hrc>
59 #include <cppunit/TestAssert.h>
60 #include <vcl/BitmapTools.hxx>
61 #include <vcl/filter/PngImageWriter.hxx>
62 #include <vcl/filter/PDFiumLibrary.hxx>
63 #include <svtools/colorcfg.hxx>
64 #include <sal/types.h>
65 #include <test/lokcallback.hxx>
71 using namespace com::sun::star
;
72 using namespace desktop
;
74 static LibreOfficeKitDocumentType
getDocumentTypeFromName(const char* pName
)
76 CPPUNIT_ASSERT_MESSAGE("Document name must be valid.", pName
!= nullptr);
78 const std::string
name(pName
);
79 CPPUNIT_ASSERT_MESSAGE("Document name must include extension.", name
.size() > 4);
81 const auto it
= name
.rfind('.');
82 if (it
!= std::string::npos
)
84 const std::string ext
= name
.substr(it
);
87 return LOK_DOCTYPE_SPREADSHEET
;
90 return LOK_DOCTYPE_PRESENTATION
;
93 CPPUNIT_ASSERT_MESSAGE("Document name must include extension.", it
!= std::string::npos
);
94 return LOK_DOCTYPE_TEXT
;
97 class DesktopLOKTest
: public UnoApiTest
100 DesktopLOKTest() : UnoApiTest(u
"/desktop/qa/data/"_ustr
),
101 m_nSelectionBeforeSearchResult(0),
102 m_nSelectionAfterSearchResult(0),
109 void readFileIntoByteVector(
110 std::u16string_view sFilename
, std::vector
<sal_uInt8
> & rByteVector
);
112 virtual void setUp() override
114 comphelper::LibreOfficeKit::setActive(true);
119 virtual void tearDown() override
123 // documents are already closed, no need to call UnoApiTest::tearDown
124 test::BootstrapFixture::tearDown();
126 comphelper::LibreOfficeKit::setActive(false);
129 std::unique_ptr
<LibLODocument_Impl
>
130 loadDocImpl(const char* pName
, LibreOfficeKitDocumentType eType
);
133 std::unique_ptr
<LibLODocument_Impl
>
134 loadDocImpl(const char* pName
);
137 std::unique_ptr
<LibLODocument_Impl
>
138 loadDocUrlImpl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
140 LibLODocument_Impl
* loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
141 LibLODocument_Impl
* loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
);
142 LibLODocument_Impl
* loadDoc(const char* pName
)
144 return loadDoc(pName
, getDocumentTypeFromName(pName
));
147 void closeDoc(std::unique_ptr
<LibLODocument_Impl
>& loDocument
);
148 void closeDoc() { closeDoc(m_pDocument
); }
149 static void callback(int nType
, const char* pPayload
, void* pData
);
150 void callbackImpl(int nType
, const char* pPayload
);
152 void testGetStyles();
154 void testCreateView();
155 void testGetFilterTypes();
156 void testGetPartPageRectangles();
157 void testSearchCalc();
158 void testSearchAllNotificationsCalc();
159 void testPaintTile();
161 void testSaveAsJsonOptions();
162 void testSaveAsCalc();
163 void testPasteWriter();
164 void testPasteWriterJPEG();
165 void testUndoWriter();
166 void testRowColumnHeaders();
167 void testHiddenRowHeaders();
168 void testCellCursor();
169 void testCommandResult();
170 void testWriterComments();
171 void testSheetOperations();
172 void testSheetSelections();
173 void testSheetDragDrop();
174 void testContextMenuCalc();
175 void testContextMenuWriter();
176 void testContextMenuImpress();
177 void testNotificationCompression();
178 void testTileInvalidationCompression();
179 void testPartInInvalidation();
180 void testBinaryCallback();
182 void testRedlineWriter();
183 void testTrackChanges();
184 void testRedlineCalc();
185 void testPaintPartTile();
186 void testPaintPartTileDifferentSchemes();
188 void testGetFontSubset();
190 void testCommentsWriter();
191 void testCommentsCalc();
192 void testCommentsImpress();
193 void testCommentsCallbacksWriter();
194 void testCommentsAddEditDeleteDraw();
195 void testCommentsInReadOnlyMode();
197 void testExtractParameter();
198 void testGetSignatureState_NonSigned();
199 void testGetSignatureState_Signed();
200 #if 0 // broken with system nss on RHEL 7
201 void testInsertCertificate_DER_ODT();
202 void testInsertCertificate_PEM_ODT();
203 void testInsertCertificate_PEM_DOCX();
205 void testSignDocument_PEM_PDF();
206 void testTextSelectionHandles();
207 void testComplexSelection();
208 void testSpellcheckerMultiView();
209 void testDialogPaste();
210 void testCalcSaveAs();
211 void testControlState();
212 void testMetricField();
213 void testMultiDocuments();
214 void testJumpCursor();
215 void testRenderSearchResult_WriterNode();
216 void testRenderSearchResult_CommonNode();
217 void testNoDuplicateTableSelection();
218 void testMultiViewTableSelection();
219 void testColorPaletteCallback();
222 CPPUNIT_TEST_SUITE(DesktopLOKTest
);
223 CPPUNIT_TEST(testGetStyles
);
224 CPPUNIT_TEST(testGetFonts
);
225 CPPUNIT_TEST(testCreateView
);
226 CPPUNIT_TEST(testGetFilterTypes
);
227 CPPUNIT_TEST(testGetPartPageRectangles
);
228 CPPUNIT_TEST(testSearchCalc
);
229 CPPUNIT_TEST(testSearchAllNotificationsCalc
);
230 CPPUNIT_TEST(testPaintTile
);
231 CPPUNIT_TEST(testSaveAs
);
232 CPPUNIT_TEST(testSaveAsJsonOptions
);
233 CPPUNIT_TEST(testSaveAsCalc
);
234 CPPUNIT_TEST(testPasteWriter
);
235 CPPUNIT_TEST(testPasteWriterJPEG
);
236 CPPUNIT_TEST(testUndoWriter
);
237 CPPUNIT_TEST(testRowColumnHeaders
);
238 CPPUNIT_TEST(testHiddenRowHeaders
);
239 CPPUNIT_TEST(testCellCursor
);
240 CPPUNIT_TEST(testCommandResult
);
241 CPPUNIT_TEST(testWriterComments
);
242 CPPUNIT_TEST(testSheetOperations
);
243 CPPUNIT_TEST(testSheetSelections
);
244 CPPUNIT_TEST(testSheetDragDrop
);
245 CPPUNIT_TEST(testContextMenuCalc
);
246 CPPUNIT_TEST(testContextMenuWriter
);
247 CPPUNIT_TEST(testContextMenuImpress
);
248 CPPUNIT_TEST(testNotificationCompression
);
249 CPPUNIT_TEST(testTileInvalidationCompression
);
250 CPPUNIT_TEST(testPartInInvalidation
);
251 CPPUNIT_TEST(testBinaryCallback
);
252 CPPUNIT_TEST(testInput
);
253 CPPUNIT_TEST(testRedlineWriter
);
254 CPPUNIT_TEST(testTrackChanges
);
255 CPPUNIT_TEST(testRedlineCalc
);
256 CPPUNIT_TEST(testPaintPartTile
);
257 CPPUNIT_TEST(testPaintPartTileDifferentSchemes
);
259 CPPUNIT_TEST(testGetFontSubset
);
261 CPPUNIT_TEST(testCommentsWriter
);
262 CPPUNIT_TEST(testCommentsCalc
);
263 CPPUNIT_TEST(testCommentsImpress
);
264 CPPUNIT_TEST(testCommentsCallbacksWriter
);
265 CPPUNIT_TEST(testCommentsAddEditDeleteDraw
);
266 CPPUNIT_TEST(testCommentsInReadOnlyMode
);
267 CPPUNIT_TEST(testRunMacro
);
268 CPPUNIT_TEST(testExtractParameter
);
269 CPPUNIT_TEST(testGetSignatureState_Signed
);
270 CPPUNIT_TEST(testGetSignatureState_NonSigned
);
272 #if 0 // broken with system nss on RHEL 7
273 CPPUNIT_TEST(testInsertCertificate_DER_ODT
);
274 CPPUNIT_TEST(testInsertCertificate_PEM_ODT
);
275 CPPUNIT_TEST(testInsertCertificate_PEM_DOCX
);
277 CPPUNIT_TEST(testSignDocument_PEM_PDF
);
279 CPPUNIT_TEST(testTextSelectionHandles
);
280 CPPUNIT_TEST(testComplexSelection
);
281 CPPUNIT_TEST(testSpellcheckerMultiView
);
282 CPPUNIT_TEST(testDialogPaste
);
283 CPPUNIT_TEST(testCalcSaveAs
);
284 CPPUNIT_TEST(testControlState
);
285 CPPUNIT_TEST(testMetricField
);
286 CPPUNIT_TEST(testMultiDocuments
);
287 CPPUNIT_TEST(testJumpCursor
);
288 CPPUNIT_TEST(testRenderSearchResult_WriterNode
);
289 CPPUNIT_TEST(testRenderSearchResult_CommonNode
);
290 CPPUNIT_TEST(testNoDuplicateTableSelection
);
291 CPPUNIT_TEST(testMultiViewTableSelection
);
292 CPPUNIT_TEST(testColorPaletteCallback
);
293 CPPUNIT_TEST(testABI
);
294 CPPUNIT_TEST_SUITE_END();
296 OString m_aTextSelection
;
297 OString m_aTextSelectionStart
;
298 OString m_aTextSelectionEnd
;
299 std::vector
<OString
> m_aSearchResultSelection
;
300 std::vector
<int> m_aSearchResultPart
;
301 int m_nSelectionBeforeSearchResult
;
302 int m_nSelectionAfterSearchResult
;
304 // for testCommandResult
305 osl::Condition m_aCommandResultCondition
;
306 OString m_aCommandResult
;
308 // for testModifiedStatus
309 osl::Condition m_aStateChangedCondition
;
313 // for testContextMenu{Calc, Writer}
314 osl::Condition m_aContextMenuCondition
;
315 boost::property_tree::ptree m_aContextMenuResult
;
317 std::unique_ptr
<LibLODocument_Impl
> m_pDocument
;
320 DesktopLOKTest::~DesktopLOKTest()
327 static Control
* GetFocusControl(vcl::Window
const * pParent
)
329 sal_uInt16 nChildren
= pParent
->GetChildCount();
330 for (sal_uInt16 nChild
= 0; nChild
< nChildren
; ++nChild
)
332 vcl::Window
* pChild
= pParent
->GetChild( nChild
);
333 Control
* pCtrl
= dynamic_cast<Control
*>(pChild
);
334 if (pCtrl
&& pCtrl
->HasControlFocus())
337 Control
* pSubCtrl
= GetFocusControl( pChild
);
344 std::unique_ptr
<LibLODocument_Impl
>
345 DesktopLOKTest::loadDocUrlImpl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
350 case LOK_DOCTYPE_TEXT
:
351 aService
= "com.sun.star.text.TextDocument";
353 case LOK_DOCTYPE_SPREADSHEET
:
354 aService
= "com.sun.star.sheet.SpreadsheetDocument";
356 case LOK_DOCTYPE_PRESENTATION
:
357 aService
= "com.sun.star.presentation.PresentationDocument";
360 CPPUNIT_ASSERT(false);
364 static int nDocumentIdCounter
= 0;
365 SfxViewShell::SetCurrentDocId(ViewShellDocId(nDocumentIdCounter
));
366 mxComponent
= loadFromDesktop(rFileURL
, aService
);
368 std::unique_ptr
<LibLODocument_Impl
> pDocument(new LibLODocument_Impl(mxComponent
, nDocumentIdCounter
));
369 ++nDocumentIdCounter
;
374 std::unique_ptr
<LibLODocument_Impl
>
375 DesktopLOKTest::loadDocImpl(const char* pName
, LibreOfficeKitDocumentType eType
)
377 OUString aFileURL
= createFileURL(OUString::createFromAscii(pName
));
378 return loadDocUrlImpl(aFileURL
, eType
);
381 std::unique_ptr
<LibLODocument_Impl
>
382 DesktopLOKTest::loadDocImpl(const char* pName
)
384 return loadDocImpl(pName
, getDocumentTypeFromName(pName
));
387 LibLODocument_Impl
* DesktopLOKTest::loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
389 m_pDocument
= loadDocUrlImpl(rFileURL
, eType
);
390 return m_pDocument
.get();
393 LibLODocument_Impl
* DesktopLOKTest::loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
)
395 m_pDocument
= loadDocImpl(pName
, eType
);
396 return m_pDocument
.get();
399 void DesktopLOKTest::closeDoc(std::unique_ptr
<LibLODocument_Impl
>& pDocument
)
403 pDocument
->pClass
->registerCallback(pDocument
.get(), nullptr, nullptr);
407 if (mxComponent
.is())
409 css::uno::Reference
<util::XCloseable
> xCloseable(mxComponent
, css::uno::UNO_QUERY_THROW
);
410 xCloseable
->close(false);
415 void DesktopLOKTest::callback(int nType
, const char* pPayload
, void* pData
)
417 static_cast<DesktopLOKTest
*>(pData
)->callbackImpl(nType
, pPayload
);
420 void DesktopLOKTest::callbackImpl(int nType
, const char* pPayload
)
424 case LOK_CALLBACK_TEXT_SELECTION
:
426 m_aTextSelection
= pPayload
;
427 if (m_aSearchResultSelection
.empty())
428 ++m_nSelectionBeforeSearchResult
;
430 ++m_nSelectionAfterSearchResult
;
433 case LOK_CALLBACK_TEXT_SELECTION_START
:
434 m_aTextSelectionStart
= pPayload
;
436 case LOK_CALLBACK_TEXT_SELECTION_END
:
437 m_aTextSelectionEnd
= pPayload
;
439 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
441 m_aSearchResultSelection
.clear();
442 boost::property_tree::ptree aTree
;
443 std::stringstream
aStream(pPayload
);
444 boost::property_tree::read_json(aStream
, aTree
);
445 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("searchResultSelection"))
447 m_aSearchResultSelection
.emplace_back(rValue
.second
.get
<std::string
>("rectangles").c_str());
448 m_aSearchResultPart
.push_back(std::atoi(rValue
.second
.get
<std::string
>("part").c_str()));
452 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
454 m_aCommandResult
= pPayload
;
455 m_aCommandResultCondition
.set();
458 case LOK_CALLBACK_STATE_CHANGED
:
460 OString
aPayload(pPayload
);
461 OString
aPrefix(".uno:ModifiedStatus="_ostr
);
462 if (aPayload
.startsWith(aPrefix
))
464 m_bModified
= aPayload
.copy(aPrefix
.getLength()).toBoolean();
465 m_aStateChangedCondition
.set();
467 else if (aPayload
.startsWith(".uno:TrackChanges=") && aPayload
.endsWith("=true"))
471 case LOK_CALLBACK_CONTEXT_MENU
:
473 m_aContextMenuResult
.clear();
474 std::stringstream
aStream(pPayload
);
475 boost::property_tree::read_json(aStream
, m_aContextMenuResult
);
476 m_aContextMenuCondition
.set();
482 void DesktopLOKTest::testGetStyles()
484 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
485 boost::property_tree::ptree aTree
;
486 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:StyleApply");
487 std::stringstream
aStream(pJSON
);
488 boost::property_tree::read_json(aStream
, aTree
);
489 CPPUNIT_ASSERT( !aTree
.empty() );
490 CPPUNIT_ASSERT_EQUAL( std::string(".uno:StyleApply"), aTree
.get_child("commandName").get_value
<std::string
>() );
492 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
493 CPPUNIT_ASSERT( !aValues
.empty() );
494 for (const auto& rPair
: aValues
)
496 if( rPair
.first
!= "ClearStyle")
498 CPPUNIT_ASSERT( !rPair
.second
.empty());
500 if (rPair
.first
!= "CharacterStyles" &&
501 rPair
.first
!= "ParagraphStyles" &&
502 rPair
.first
!= "FrameStyles" &&
503 rPair
.first
!= "PageStyles" &&
504 rPair
.first
!= "NumberingStyles" &&
505 rPair
.first
!= "CellStyles" &&
506 rPair
.first
!= "ShapeStyles" &&
507 rPair
.first
!= "TableStyles" &&
508 rPair
.first
!= "HeaderFooter" &&
509 rPair
.first
!= "Commands")
511 CPPUNIT_FAIL("Unknown style family: " + rPair
.first
);
517 void DesktopLOKTest::testGetFonts()
519 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
520 boost::property_tree::ptree aTree
;
521 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CharFontName");
522 std::stringstream
aStream(pJSON
);
523 boost::property_tree::read_json(aStream
, aTree
);
524 CPPUNIT_ASSERT( !aTree
.empty() );
525 CPPUNIT_ASSERT_EQUAL( std::string(".uno:CharFontName"), aTree
.get_child("commandName").get_value
<std::string
>() );
527 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
528 CPPUNIT_ASSERT( !aValues
.empty() );
529 for (const auto& rPair
: aValues
)
531 // check that we have font sizes available for each font
532 CPPUNIT_ASSERT( !rPair
.second
.empty());
537 void DesktopLOKTest::testCreateView()
539 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
540 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
542 int nId0
= pDocument
->m_pDocumentClass
->getView(pDocument
);
543 int nId1
= pDocument
->m_pDocumentClass
->createView(pDocument
);
544 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
546 // Test getViewIds().
547 std::vector
<int> aViewIds(2);
548 CPPUNIT_ASSERT(pDocument
->m_pDocumentClass
->getViewIds(pDocument
, aViewIds
.data(), aViewIds
.size()));
549 CPPUNIT_ASSERT_EQUAL(nId0
, aViewIds
[0]);
550 CPPUNIT_ASSERT_EQUAL(nId1
, aViewIds
[1]);
552 // Make sure the created view is the active one, then switch to the old
554 CPPUNIT_ASSERT_EQUAL(nId1
, pDocument
->m_pDocumentClass
->getView(pDocument
));
555 pDocument
->m_pDocumentClass
->setView(pDocument
, nId0
);
556 CPPUNIT_ASSERT_EQUAL(nId0
, pDocument
->m_pDocumentClass
->getView(pDocument
));
558 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nId1
);
559 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
562 void DesktopLOKTest::testGetPartPageRectangles()
564 // Test that we get as many page rectangles as expected: blank document is
566 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
567 char* pRectangles
= pDocument
->pClass
->getPartPageRectangles(pDocument
);
568 OUString sRectangles
= OUString::fromUtf8(pRectangles
);
570 std::vector
<OUString
> aRectangles
;
571 sal_Int32 nIndex
= 0;
574 OUString aRectangle
= sRectangles
.getToken(0, ';', nIndex
);
575 if (!aRectangle
.isEmpty())
576 aRectangles
.push_back(aRectangle
);
579 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aRectangles
.size());
584 void DesktopLOKTest::testGetFilterTypes()
586 LibLibreOffice_Impl aOffice
;
587 char* pJSON
= aOffice
.m_pOfficeClass
->getFilterTypes(&aOffice
);
589 std::stringstream
aStream(pJSON
);
590 boost::property_tree::ptree aTree
;
591 boost::property_tree::read_json(aStream
, aTree
);
593 CPPUNIT_ASSERT(!aTree
.empty());
594 CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.oasis.opendocument.text"), aTree
.get_child("writer8").get_child("MediaType").get_value
<std::string
>());
598 void DesktopLOKTest::testSearchCalc()
600 LibLibreOffice_Impl aOffice
;
601 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
602 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
603 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
605 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
607 {"SearchItem.SearchString", uno::Any(u
"foo"_ustr
)},
608 {"SearchItem.Backward", uno::Any(false)},
609 {"SearchItem.Command", uno::Any(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
611 dispatchCommand(mxComponent
, u
".uno:ExecuteSearch"_ustr
, aPropertyValues
);
613 std::vector
<OString
> aSelections
;
614 sal_Int32 nIndex
= 0;
617 OString aToken
= m_aTextSelection
.getToken(0, ';', nIndex
);
618 aSelections
.push_back(aToken
);
619 } while (nIndex
>= 0);
620 // This was 1, find-all only found one match.
621 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aSelections
.size());
622 // Make sure that we get exactly as many rectangle lists as matches.
623 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), m_aSearchResultSelection
.size());
624 // Result is on the first sheet.
625 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart
[0]);
628 void DesktopLOKTest::testSearchAllNotificationsCalc()
630 LibLibreOffice_Impl aOffice
;
631 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
632 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
633 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
635 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
637 {"SearchItem.SearchString", uno::Any(u
"foo"_ustr
)},
638 {"SearchItem.Backward", uno::Any(false)},
639 {"SearchItem.Command", uno::Any(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
641 dispatchCommand(mxComponent
, u
".uno:ExecuteSearch"_ustr
, aPropertyValues
);
643 // This was 1, make sure that we get no notifications about selection changes during search.
644 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult
);
645 // But we do get the selection afterwards.
646 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult
> 0);
649 void DesktopLOKTest::testPaintTile()
651 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
652 int nCanvasWidth
= 100;
653 int nCanvasHeight
= 300;
654 sal_Int32 nStride
= cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32
, nCanvasWidth
);
655 std::vector
<unsigned char> aBuffer(nStride
* nCanvasHeight
);
658 int nTileWidth
= 1000;
659 int nTileHeight
= 3000;
661 // This used to crash: paintTile() implementation did not handle
662 // nCanvasWidth != nCanvasHeight correctly, as usually both are just always
664 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
666 // This crashed in OutputDevice::DrawDeviceAlphaBitmap().
671 aBuffer
.resize(nCanvasWidth
* nCanvasHeight
* 4);
672 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
675 void DesktopLOKTest::testSaveAs()
677 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
678 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
681 void DesktopLOKTest::testSaveAsJsonOptions()
683 // Given a document with 3 pages:
684 LibLODocument_Impl
* pDocument
= loadDoc("3page.odg");
686 // When exporting that document to PDF, skipping the first page:
687 OString
aOptions("{\"PageRange\":{\"type\":\"string\",\"value\":\"2-\"}}"_ostr
);
688 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "pdf", aOptions
.getStr()));
690 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
694 // Then make sure the resulting PDF has 2 pages:
695 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
697 // Without the accompanying fix in place, this test would have failed with:
700 // i.e. FilterOptions was ignored.
701 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
704 void DesktopLOKTest::testSaveAsCalc()
706 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
707 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
710 void DesktopLOKTest::testPasteWriter()
712 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
713 OString
aText("hello"_ostr
);
715 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
717 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
718 Scheduler::ProcessEventsToIdle();
719 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
720 CPPUNIT_ASSERT_EQUAL("hello"_ostr
, OString(pText
));
723 // textt/plain should be rejected.
724 CPPUNIT_ASSERT(!pDocument
->pClass
->paste(pDocument
, "textt/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
725 // Writer is expected to support text/html.
726 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aText
.getStr(), aText
.getLength()));
728 // Overwrite doc contents with a HTML paste.
729 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
730 Scheduler::ProcessEventsToIdle();
731 OString
aComment("foo <!-- bar --> baz"_ostr
);
732 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aComment
.getStr(), aComment
.getLength()));
734 // Check if we have a comment.
735 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
736 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
737 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
738 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
739 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
740 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
741 CPPUNIT_ASSERT_EQUAL(u
"Text"_ustr
, xTextPortion
->getPropertyValue(u
"TextPortionType"_ustr
).get
<OUString
>());
742 // Without the accompanying fix in place, this test would have failed, as we had a comment
743 // between "foo" and "baz".
744 CPPUNIT_ASSERT(!xTextPortionEnumeration
->hasMoreElements());
747 void DesktopLOKTest::testPasteWriterJPEG()
749 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
751 OUString aFileURL
= createFileURL(u
"paste.jpg");
752 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
753 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
755 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
757 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
758 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
759 // This was 0, JPEG was not handled as a format for clipboard paste.
760 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(1), xDrawPage
->getCount());
762 uno::Reference
<beans::XPropertySet
> xShape(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
763 // This was text::TextContentAnchorType_AT_PARAGRAPH.
764 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER
, xShape
->getPropertyValue(u
"AnchorType"_ustr
).get
<text::TextContentAnchorType
>());
766 // Delete the pasted picture, and paste again with a custom anchor type.
767 uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY_THROW
)->dispose();
768 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
770 {"AnchorType", uno::Any(static_cast<sal_uInt16
>(text::TextContentAnchorType_AT_CHARACTER
))},
772 dispatchCommand(mxComponent
, u
".uno:Paste"_ustr
, aPropertyValues
);
773 xShape
.set(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
774 // This was text::TextContentAnchorType_AS_CHARACTER, AnchorType argument was ignored.
775 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AT_CHARACTER
, xShape
->getPropertyValue(u
"AnchorType"_ustr
).get
<text::TextContentAnchorType
>());
778 void DesktopLOKTest::testUndoWriter()
780 // Load a Writer document and press a key.
781 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
782 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
783 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
784 Scheduler::ProcessEventsToIdle();
786 boost::property_tree::ptree aTree
;
787 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:Undo");
788 std::stringstream
aStream(pJSON
);
790 CPPUNIT_ASSERT(!aStream
.str().empty());
791 boost::property_tree::read_json(aStream
, aTree
);
792 // Make sure that pressing a key creates exactly one undo action.
793 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("actions").size());
796 void DesktopLOKTest::testRowColumnHeaders()
804 * "size": "254.987250637468",
808 * "size": "509.974501274936",
814 * "size": "1274.93625318734",
818 * "size": "2549.87250637468",
824 * "size" defines the bottom/right boundary of a row/column in twips (size between 0 and boundary)
825 * "text" has the header label in UTF-8
827 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
829 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
831 tools::Long nWidth
= 0;
832 tools::Long nHeight
= 0;
833 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
834 tools::Long nX
= rtl::math::round(nWidth
/ 4.0);
835 tools::Long nY
= rtl::math::round(nHeight
/ 4.0);
836 nWidth
= rtl::math::round(nWidth
/ 2.0);
837 nHeight
= rtl::math::round(nHeight
/ 2.0);
839 std::stringstream aPayload
;
840 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
842 boost::property_tree::ptree aTree
;
843 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
844 std::stringstream
aStream(pJSON
);
847 CPPUNIT_ASSERT(!aStream
.str().empty());
849 boost::property_tree::read_json(aStream
, aTree
);
850 sal_Int32 nPrevious
= 0;
851 bool bFirstHeader
= true;
852 bool bNotEnoughHeaders
= true;
853 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
855 sal_Int32 nSize
= o3tl::toInt32(rValue
.second
.get
<std::string
>("size"));
856 nSize
= o3tl::convert(nSize
, o3tl::Length::px
, o3tl::Length::twip
);
857 OString
aText(rValue
.second
.get
<std::string
>("text"));
861 CPPUNIT_ASSERT(nSize
<= nY
);
862 CPPUNIT_ASSERT_EQUAL("10"_ostr
, aText
);
863 bFirstHeader
= false;
867 CPPUNIT_ASSERT(nSize
> 0);
868 CPPUNIT_ASSERT(nPrevious
< nSize
);
869 if (nSize
> nY
+ nHeight
)
871 bNotEnoughHeaders
= false;
877 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
881 bNotEnoughHeaders
= true;
882 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("columns"))
884 sal_Int32 nSize
= o3tl::toInt32(rValue
.second
.get
<std::string
>("size"));
885 nSize
= o3tl::convert(nSize
, o3tl::Length::px
, o3tl::Length::twip
);
886 OString
aText(rValue
.second
.get
<std::string
>("text"));
889 CPPUNIT_ASSERT(nSize
<= nX
);
890 CPPUNIT_ASSERT_EQUAL("3"_ostr
, aText
);
891 bFirstHeader
= false;
895 CPPUNIT_ASSERT(nSize
> 0);
896 CPPUNIT_ASSERT(nPrevious
< nSize
);
897 if (nSize
> nX
+ nWidth
)
899 bNotEnoughHeaders
= false;
905 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
908 void DesktopLOKTest::testHiddenRowHeaders()
910 LibLODocument_Impl
* pDocument
= loadDoc("hidden-row.ods");
912 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
914 tools::Long
const nX
= 0;
915 tools::Long
const nY
= 0;
916 tools::Long nWidth
= 0;
917 tools::Long nHeight
= 0;
918 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
920 std::stringstream aPayload
;
921 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
923 boost::property_tree::ptree aTree
;
924 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
925 std::stringstream
aStream(pJSON
);
926 CPPUNIT_ASSERT(!aStream
.str().empty());
928 boost::property_tree::read_json(aStream
, aTree
);
930 sal_Int32 nPrevious
= 0;
931 sal_Int32 nIndex
= 0;
932 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
934 sal_Int32 nSize
= o3tl::toInt32(rValue
.second
.get
<std::string
>("size"));
938 // nSize was 510, nPrevious was 255, i.e. hidden row wasn't reported as 0 height.
939 CPPUNIT_ASSERT_EQUAL(nPrevious
, nSize
);
946 void DesktopLOKTest::testCellCursor()
948 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
950 boost::property_tree::ptree aTree
;
952 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CellCursor?tileWidth=1&tileHeight=1&outputWidth=1&outputHeight=1");
954 std::stringstream
aStream(pJSON
);
956 CPPUNIT_ASSERT(!aStream
.str().empty());
958 boost::property_tree::read_json(aStream
, aTree
);
960 OString
aRectangle(aTree
.get
<std::string
>("commandValues"));
961 // cell cursor geometry + col + row
962 CPPUNIT_ASSERT_EQUAL("0, 0, 1274, 254, 0, 0"_ostr
, aRectangle
);
965 void DesktopLOKTest::testCommandResult()
967 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
969 // the postUnoCommand() is supposed to be async, let's test it safely
970 // [no idea if it is async in reality - most probably we are operating
971 // under some solar mutex or something anyway ;-) - but...]
972 TimeValue aTimeValue
= { 2 , 0 }; // 2 seconds max
974 // nothing is triggered when we have no callback yet, we just time out on
975 // the condition var.
976 m_aCommandResultCondition
.reset();
977 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
978 Scheduler::ProcessEventsToIdle();
979 m_aCommandResultCondition
.wait(aTimeValue
);
981 CPPUNIT_ASSERT(m_aCommandResult
.isEmpty());
983 // but we get some real values when the callback is set up
984 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
986 m_aCommandResultCondition
.reset();
987 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
988 Scheduler::ProcessEventsToIdle();
989 m_aCommandResultCondition
.wait(aTimeValue
);
991 boost::property_tree::ptree aTree
;
992 std::stringstream
aStream((std::string(m_aCommandResult
)));
993 boost::property_tree::read_json(aStream
, aTree
);
995 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), aTree
.get_child("commandName").get_value
<std::string
>());
996 CPPUNIT_ASSERT_EQUAL(true, aTree
.get_child("success").get_value
<bool>());
999 void DesktopLOKTest::testWriterComments()
1001 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1002 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1003 uno::Reference
<awt::XReschedule
> xToolkit
= css::awt::Toolkit::create(comphelper::getProcessComponentContext());
1005 // Insert a comment at the beginning of the document and wait till the main
1006 // loop grabs the focus, so characters end up in the annotation window.
1007 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1008 m_aCommandResultCondition
.reset();
1009 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", nullptr, true);
1010 Scheduler::ProcessEventsToIdle();
1011 m_aCommandResultCondition
.wait(aTimeValue
);
1012 CPPUNIT_ASSERT(!m_aCommandResult
.isEmpty());
1013 xToolkit
->reschedule();
1015 // Test that we have a comment.
1016 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
1017 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
1018 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
1019 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
1020 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
1021 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
1022 CPPUNIT_ASSERT_EQUAL(u
"Annotation"_ustr
, xTextPortion
->getPropertyValue(u
"TextPortionType"_ustr
).get
<OUString
>());
1024 // Type "test" and finish editing.
1025 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1026 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'e', 0);
1027 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 's', 0);
1028 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1029 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, css::awt::Key::ESCAPE
);
1030 Scheduler::ProcessEventsToIdle();
1032 // Test that the typed characters ended up in the right window.
1033 auto xTextField
= xTextPortion
->getPropertyValue(u
"TextField"_ustr
).get
< uno::Reference
<beans::XPropertySet
> >();
1034 // This was empty, typed characters ended up in the body text.
1035 CPPUNIT_ASSERT_EQUAL(u
"test"_ustr
, xTextField
->getPropertyValue(u
"Content"_ustr
).get
<OUString
>());
1038 void DesktopLOKTest::testTrackChanges()
1040 // Load a document and create two views.
1041 LibLibreOffice_Impl aOffice
;
1042 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1043 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1044 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1045 pDocument
->pClass
->createView(pDocument
);
1046 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1047 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1048 Scheduler::ProcessEventsToIdle();
1050 // Enable track changes and assert that both views get notified.
1051 m_nTrackChanges
= 0;
1052 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:TrackChanges", nullptr, false);
1053 Scheduler::ProcessEventsToIdle();
1054 // This was 1, only the active view was notified.
1055 CPPUNIT_ASSERT_EQUAL(2, m_nTrackChanges
);
1058 void DesktopLOKTest::testSheetOperations()
1060 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
1062 // insert the last sheet
1063 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
1064 "{ \"Name\": { \"type\": \"string\", \"value\": \"LastSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 0 } }", false);
1066 // insert the first sheet
1067 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
1068 "{ \"Name\": { \"type\": \"string\", \"value\": \"FirstSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 1 } }", false);
1070 // rename the \"Sheet1\" (2nd now) to \"Renamed\"
1071 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Name",
1072 "{ \"Name\": { \"type\": \"string\", \"value\": \"Renamed\" }, \"Index\": { \"type\": \"long\", \"value\": 2 } }", false);
1074 // delete the \"Sheet2\" (3rd)
1075 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Remove",
1076 "{ \"Index\": { \"type\": \"long\", \"value\": 3 } }", false);
1078 Scheduler::ProcessEventsToIdle();
1079 CPPUNIT_ASSERT_EQUAL(6, pDocument
->pClass
->getParts(pDocument
));
1081 std::vector
<OString
> aExpected
= { "FirstSheet"_ostr
, "Renamed"_ostr
, "Sheet3"_ostr
, "Sheet4"_ostr
, "Sheet5"_ostr
, "LastSheet"_ostr
};
1082 for (int i
= 0; i
< 6; ++i
)
1084 char* pPartName
= pDocument
->pClass
->getPartName(pDocument
, i
);
1085 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], OString(pPartName
));
1090 void DesktopLOKTest::testSheetSelections()
1092 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods", LOK_DOCTYPE_SPREADSHEET
);
1093 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1094 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1097 * Check if selection data is correct
1102 int const col2
= 2200;
1103 int const col3
= 3300;
1107 // Select row 5 from column 1 through column 5
1108 pDocument
->pClass
->postMouseEvent(pDocument
,
1109 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1112 pDocument
->pClass
->postMouseEvent(pDocument
,
1113 LOK_MOUSEEVENT_MOUSEMOVE
,
1116 pDocument
->pClass
->postMouseEvent(pDocument
,
1117 LOK_MOUSEEVENT_MOUSEMOVE
,
1120 pDocument
->pClass
->postMouseEvent(pDocument
,
1121 LOK_MOUSEEVENT_MOUSEMOVE
,
1124 pDocument
->pClass
->postMouseEvent(pDocument
,
1125 LOK_MOUSEEVENT_MOUSEMOVE
,
1128 pDocument
->pClass
->postMouseEvent(pDocument
,
1129 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1132 Scheduler::ProcessEventsToIdle();
1134 // Copy the contents and check if matches expected data
1136 char* pUsedMimeType
= nullptr;
1137 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1138 std::vector
<long> aExpected
= {5, 6, 7, 8, 9};
1139 std::istringstream
iss(pCopiedContent
);
1140 for (const long nIndex
: aExpected
)
1144 CPPUNIT_ASSERT_EQUAL(nIndex
, strtol(token
.c_str(), nullptr, 10));
1147 free(pUsedMimeType
);
1148 free(pCopiedContent
);
1152 * Check if clicking inside the selection deselects the whole selection
1155 // Click at row5, col4
1156 pDocument
->pClass
->postMouseEvent(pDocument
,
1157 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1160 pDocument
->pClass
->postMouseEvent(pDocument
,
1161 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1164 Scheduler::ProcessEventsToIdle();
1166 // Selected text should get deselected and copying should give us
1167 // content of only one cell, now
1169 char* pUsedMimeType
= nullptr;
1170 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1171 std::vector
<long> aExpected
= { 8 };
1172 std::istringstream
iss(pCopiedContent
);
1173 for (const long nIndex
: aExpected
)
1177 CPPUNIT_ASSERT_EQUAL(nIndex
, strtol(token
.c_str(), nullptr, 10));
1180 free(pUsedMimeType
);
1181 free(pCopiedContent
);
1185 void DesktopLOKTest::testSheetDragDrop()
1187 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods", LOK_DOCTYPE_SPREADSHEET
);
1188 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1189 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1198 // Select row 01 from column 01 through column 05
1199 pDocument
->pClass
->postMouseEvent(pDocument
,
1200 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1203 pDocument
->pClass
->postMouseEvent(pDocument
,
1204 LOK_MOUSEEVENT_MOUSEMOVE
,
1207 pDocument
->pClass
->postMouseEvent(pDocument
,
1208 LOK_MOUSEEVENT_MOUSEMOVE
,
1211 pDocument
->pClass
->postMouseEvent(pDocument
,
1212 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1216 Scheduler::ProcessEventsToIdle();
1218 SfxViewShell
* pViewShell
= SfxViewShell::Current();
1219 SfxViewFrame
& rViewFrame
= pViewShell
->GetViewFrame();
1222 css::uno::Any aValue
;
1223 css::util::URL aURL
;
1224 std::unique_ptr
<SfxPoolItem
> pState
;
1226 aURL
.Protocol
= ".uno:";
1227 aURL
.Complete
= ".uno:Address";
1228 aURL
.Path
= "Address";
1229 aURL
.Main
= ".uno:Address";
1231 rViewFrame
.GetBindings().QueryState(rViewFrame
.GetBindings().QuerySlotId(aURL
), pState
);
1232 pState
->QueryValue(aValue
);
1234 CPPUNIT_ASSERT_EQUAL(u
"Sheet5.A1:E1"_ustr
, sValue
);
1237 // Check selection content
1239 char* pMimeType
= nullptr;
1240 char* pContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pMimeType
);
1241 std::vector
<long> aExpected
= {1, 2, 3, 4, 5};
1242 std::istringstream
aContent(pContent
);
1244 for (const long nIndex
: aExpected
)
1247 CPPUNIT_ASSERT_EQUAL(nIndex
, strtol(token
.c_str(), nullptr, 10));
1255 pDocument
->pClass
->postMouseEvent(pDocument
,
1256 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1259 pDocument
->pClass
->postMouseEvent(pDocument
,
1260 LOK_MOUSEEVENT_MOUSEMOVE
,
1263 pDocument
->pClass
->postMouseEvent(pDocument
,
1264 LOK_MOUSEEVENT_MOUSEMOVE
,
1267 pDocument
->pClass
->postMouseEvent(pDocument
,
1268 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1272 Scheduler::ProcessEventsToIdle();
1274 SfxViewShell
* pViewShell
= SfxViewShell::Current();
1275 SfxViewFrame
& rViewFrame
= pViewShell
->GetViewFrame();
1278 css::uno::Any aValue
;
1279 css::util::URL aURL
;
1280 std::unique_ptr
<SfxPoolItem
> pState
;
1282 aURL
.Protocol
= ".uno:";
1283 aURL
.Complete
= ".uno:Address";
1284 aURL
.Path
= "Address";
1285 aURL
.Main
= ".uno:Address";
1287 rViewFrame
.GetBindings().QueryState(rViewFrame
.GetBindings().QuerySlotId(aURL
), pState
);
1288 pState
->QueryValue(aValue
);
1290 CPPUNIT_ASSERT_EQUAL(u
"Sheet5.D1:H1"_ustr
, sValue
);
1293 // Check selection content
1295 char* pMimeType
= nullptr;
1296 char* pContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pMimeType
);
1297 std::vector
<long> aExpected
= {1, 2, 3, 4, 5};
1298 std::istringstream
aContent(pContent
);
1300 for (const long nIndex
: aExpected
)
1303 CPPUNIT_ASSERT_EQUAL(nIndex
, strtol(token
.c_str(), nullptr, 10));
1313 void verifyContextMenuStructure(boost::property_tree::ptree
& aRoot
)
1315 for (const auto& aItemPair
: aRoot
)
1317 // This is an array, so no key
1318 CPPUNIT_ASSERT_EQUAL(aItemPair
.first
, std::string(""));
1320 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1321 boost::optional
<boost::property_tree::ptree
&> aText
= aItemValue
.get_child_optional("text");
1322 boost::optional
<boost::property_tree::ptree
&> aType
= aItemValue
.get_child_optional("type");
1323 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1324 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aItemValue
.get_child_optional("menu");
1325 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aItemValue
.get_child_optional("enabled");
1326 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aItemValue
.get_child_optional("checktype");
1327 boost::optional
<boost::property_tree::ptree
&> aChecked
= aItemValue
.get_child_optional("checked");
1329 // type is omnipresent
1330 CPPUNIT_ASSERT( aType
);
1332 // separator doesn't have any other attribs
1333 if ( aType
.get().data() == "separator" )
1335 CPPUNIT_ASSERT( !aText
);
1336 CPPUNIT_ASSERT( !aCommand
);
1337 CPPUNIT_ASSERT( !aSubmenu
);
1338 CPPUNIT_ASSERT( !aEnabled
);
1339 CPPUNIT_ASSERT( !aChecktype
);
1340 CPPUNIT_ASSERT( !aChecked
);
1342 else if ( aType
.get().data() == "command" )
1344 CPPUNIT_ASSERT( aCommand
);
1345 CPPUNIT_ASSERT( aText
);
1347 else if ( aType
.get().data() == "menu")
1349 CPPUNIT_ASSERT( aSubmenu
);
1350 CPPUNIT_ASSERT( aText
);
1351 verifyContextMenuStructure( aSubmenu
.get() );
1356 CPPUNIT_ASSERT( aChecktype
.get().data() == "radio" ||
1357 aChecktype
.get().data() == "checkmark" ||
1358 aChecktype
.get().data() == "auto" );
1360 CPPUNIT_ASSERT( aChecked
);
1361 CPPUNIT_ASSERT( aChecked
.get().data() == "true" || aChecked
.get().data() == "false" );
1367 boost::optional
<boost::property_tree::ptree
>
1368 getContextMenuItem(boost::property_tree::ptree
& aMenu
, std::string
const & unoSelector
)
1370 boost::optional
<boost::property_tree::ptree
> aMenuItem
;
1371 for (const auto& aItemPair
: aMenu
)
1373 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1375 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1376 if (aCommand
&& aCommand
.get().data() == unoSelector
)
1378 aMenuItem
= aItemValue
;
1386 } // end anonymous namespace
1388 void DesktopLOKTest::testContextMenuCalc()
1390 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
1391 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1392 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1395 Point
aPointOnImage(1150, 1100);
1396 pDocument
->pClass
->postMouseEvent(pDocument
,
1397 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1398 aPointOnImage
.X(), aPointOnImage
.Y(),
1400 Scheduler::ProcessEventsToIdle();
1402 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1403 m_aContextMenuCondition
.wait(aTimeValue
);
1405 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1406 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1407 CPPUNIT_ASSERT( aMenu
);
1408 verifyContextMenuStructure( aMenu
.get() );
1410 // tests for calc specific context menu
1413 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1414 CPPUNIT_ASSERT(aMenuItem
);
1416 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1417 CPPUNIT_ASSERT(aEnabled
);
1418 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1423 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1424 CPPUNIT_ASSERT(aMenuItem
);
1426 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1427 CPPUNIT_ASSERT(aEnabled
);
1428 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1433 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1434 CPPUNIT_ASSERT(aMenuItem
);
1436 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1437 CPPUNIT_ASSERT(aEnabled
);
1438 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1441 // Remove hyperlink is disabled
1443 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:RemoveHyperlink");
1444 CPPUNIT_ASSERT(aMenuItem
);
1446 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1447 CPPUNIT_ASSERT(aEnabled
);
1448 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1451 // open hyperlink is disabled
1453 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:OpenHyperlinkOnCursor");
1454 CPPUNIT_ASSERT(aMenuItem
);
1456 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1457 CPPUNIT_ASSERT(aEnabled
);
1458 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1461 // checkbutton tests
1463 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:AnchorMenu");
1464 CPPUNIT_ASSERT(aMenuItem
);
1466 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1467 CPPUNIT_ASSERT(aSubmenu
);
1469 boost::optional
<boost::property_tree::ptree
> aMenuItemToPage
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToPage");
1470 CPPUNIT_ASSERT(aMenuItemToPage
);
1472 boost::optional
<boost::property_tree::ptree
> aMenuItemToCell
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToCell");
1473 CPPUNIT_ASSERT(aMenuItemToCell
);
1475 // these are radio buttons
1476 boost::optional
<boost::property_tree::ptree
&> aChecktypeToPage
= aMenuItemToPage
.get().get_child_optional("checktype");
1477 CPPUNIT_ASSERT(aChecktypeToPage
);
1478 CPPUNIT_ASSERT_EQUAL(aChecktypeToPage
.get().data(), std::string("radio"));
1480 boost::optional
<boost::property_tree::ptree
&> aChecktypeToCell
= aMenuItemToCell
.get().get_child_optional("checktype");
1481 CPPUNIT_ASSERT(aChecktypeToCell
);
1482 CPPUNIT_ASSERT_EQUAL(aChecktypeToCell
.get().data(), std::string("radio"));
1484 // ToPage is checked
1485 boost::optional
<boost::property_tree::ptree
&> aCheckedToPage
= aMenuItemToPage
.get().get_child_optional("checked");
1486 CPPUNIT_ASSERT(aCheckedToPage
);
1487 CPPUNIT_ASSERT_EQUAL(aCheckedToPage
.get().data(), std::string("true"));
1489 // ToCell is unchecked
1490 boost::optional
<boost::property_tree::ptree
&> aCheckedToCell
= aMenuItemToCell
.get().get_child_optional("checked");
1491 CPPUNIT_ASSERT(aCheckedToCell
);
1492 CPPUNIT_ASSERT_EQUAL(aCheckedToCell
.get().data(), std::string("false"));
1496 void DesktopLOKTest::testContextMenuWriter()
1498 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1499 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1500 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1502 Point
aRandomPoint(1150, 1100);
1503 pDocument
->pClass
->postMouseEvent(pDocument
,
1504 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1505 aRandomPoint
.X(), aRandomPoint
.Y(),
1507 Scheduler::ProcessEventsToIdle();
1509 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1510 m_aContextMenuCondition
.wait(aTimeValue
);
1512 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1513 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1514 CPPUNIT_ASSERT( aMenu
);
1515 verifyContextMenuStructure( aMenu
.get() );
1517 // tests for writer specific context menu
1520 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1521 CPPUNIT_ASSERT(aMenuItem
);
1523 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1524 CPPUNIT_ASSERT(aEnabled
);
1525 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1530 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1531 CPPUNIT_ASSERT(aMenuItem
);
1533 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1534 CPPUNIT_ASSERT(aEnabled
);
1535 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1540 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1541 CPPUNIT_ASSERT(aMenuItem
);
1543 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1544 CPPUNIT_ASSERT(aEnabled
);
1545 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1549 void DesktopLOKTest::testContextMenuImpress()
1551 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp", LOK_DOCTYPE_PRESENTATION
);
1552 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1553 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1555 // random point where we don't hit an underlying comment or text box
1556 Point
aRandomPoint(10, 1150);
1557 pDocument
->pClass
->postMouseEvent(pDocument
,
1558 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1559 aRandomPoint
.X(), aRandomPoint
.Y(),
1561 Scheduler::ProcessEventsToIdle();
1563 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1564 m_aContextMenuCondition
.wait(aTimeValue
);
1566 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1567 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1568 CPPUNIT_ASSERT( aMenu
);
1569 verifyContextMenuStructure( aMenu
.get() );
1571 // tests for impress specific context menu
1574 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1575 CPPUNIT_ASSERT(aMenuItem
);
1577 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1578 CPPUNIT_ASSERT(aEnabled
);
1579 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1584 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1585 CPPUNIT_ASSERT(aMenuItem
);
1587 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1588 CPPUNIT_ASSERT(aEnabled
);
1589 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1594 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1595 CPPUNIT_ASSERT(aMenuItem
);
1597 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1598 CPPUNIT_ASSERT(aEnabled
);
1599 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1602 // SaveBackground is disabled
1604 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SaveBackground");
1605 CPPUNIT_ASSERT(aMenuItem
);
1607 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1608 CPPUNIT_ASSERT(aEnabled
);
1609 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1612 // checkbutton tests
1614 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:ShowRuler");
1615 CPPUNIT_ASSERT(aMenuItem
);
1617 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aMenuItem
.get().get_child_optional("checktype");
1618 CPPUNIT_ASSERT(aChecktype
);
1619 CPPUNIT_ASSERT_EQUAL(aChecktype
.get().data(), std::string("checkmark"));
1621 boost::optional
<boost::property_tree::ptree
&> aChecked
= aMenuItem
.get().get_child_optional("checked");
1622 CPPUNIT_ASSERT(aChecked
);
1623 CPPUNIT_ASSERT_EQUAL(aChecked
.get().data(), std::string("false"));
1626 // Checkbutton tests inside SnapLines submenu
1628 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SnapLinesMenu");
1629 CPPUNIT_ASSERT(aMenuItem
);
1631 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1632 CPPUNIT_ASSERT(aSubmenu
);
1634 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpVis
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesVisible");
1635 CPPUNIT_ASSERT(aMenuItemHelpVis
);
1637 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpUse
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesUse");
1638 CPPUNIT_ASSERT(aMenuItemHelpUse
);
1640 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpFront
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesFront");
1641 CPPUNIT_ASSERT(aMenuItemHelpFront
);
1643 // these are checkmarks
1644 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checktype");
1645 CPPUNIT_ASSERT(aChecktypeHelpVis
);
1646 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpVis
.get().data(), std::string("checkmark"));
1648 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checktype");
1649 CPPUNIT_ASSERT(aChecktypeHelpUse
);
1650 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpUse
.get().data(), std::string("checkmark"));
1652 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checktype");
1653 CPPUNIT_ASSERT(aChecktypeHelpFront
);
1654 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpFront
.get().data(), std::string("checkmark"));
1656 // HelplineVisible is unchecked
1657 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checked");
1658 CPPUNIT_ASSERT(aCheckedHelpVis
);
1659 CPPUNIT_ASSERT_EQUAL(aCheckedHelpVis
.get().data(), std::string("false"));
1661 // HelplineUse is checked
1662 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checked");
1663 CPPUNIT_ASSERT(aCheckedHelpUse
);
1664 CPPUNIT_ASSERT_EQUAL(aCheckedHelpUse
.get().data(), std::string("true"));
1666 // HelplineFront is checked
1667 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checked");
1668 CPPUNIT_ASSERT(aCheckedHelpFront
);
1669 CPPUNIT_ASSERT_EQUAL(aCheckedHelpFront
.get().data(), std::string("true"));
1673 static void callbackCompressionTest(const int type
, const char* payload
, void* data
)
1675 std::vector
<std::tuple
<int, std::string
>>* notifs
= static_cast<std::vector
<std::tuple
<int, std::string
>>*>(data
);
1676 notifs
->emplace_back(type
, std::string(payload
? payload
: "(nil)"));
1679 void DesktopLOKTest::testNotificationCompression()
1681 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1682 std::vector
<std::tuple
<int, std::string
>> notifs
;
1683 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1684 handler
->setViewId(SfxLokHelper::getView());
1686 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""_ostr
); // 0
1687 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"_ostr
); // Superseded.
1688 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""_ostr
); // Should be dropped.
1689 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"_ostr
); // 1
1690 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"_ostr
); // Should be dropped.
1691 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""_ostr
); // Superseded.
1692 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""_ostr
); // 2
1693 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:Bold"_ostr
); // 3
1694 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""_ostr
); // 4
1695 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"_ostr
); // 5
1696 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"_ostr
); // Should be dropped.
1697 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"_ostr
); // Should be dropped.
1698 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"_ostr
); // Should be dropped.
1699 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"_ostr
); // Superseded.
1700 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"_ostr
); // Superseded.
1701 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"_ostr
); // Superseded.
1702 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"_ostr
); // Should be dropped.
1703 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"_ostr
); // Should be dropped.
1704 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""_ostr
); // 7
1705 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"_ostr
); // 8
1706 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"_ostr
); // 9
1707 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"_ostr
); // 10
1708 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""_ostr
); // 11
1709 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"_ostr
); // Should be dropped.
1710 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"_ostr
); // 12
1711 handler
->queue(LOK_CALLBACK_SET_PART
, "1"_ostr
); // 13
1712 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=20"_ostr
); // Superseded
1713 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""_ostr
); // Should be dropped.
1714 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"_ostr
); // Should be dropped.
1715 handler
->queue(LOK_CALLBACK_SET_PART
, "1"_ostr
); // Should be dropped.
1716 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=1"_ostr
); // 14
1718 Scheduler::ProcessEventsToIdle();
1720 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(14), notifs
.size());
1723 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
), std::get
<0>(notifs
[i
]));
1724 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1726 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1727 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1729 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1730 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1732 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1733 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), std::get
<1>(notifs
[i
++]));
1735 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1736 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1738 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_MOUSE_POINTER
), std::get
<0>(notifs
[i
]));
1739 CPPUNIT_ASSERT_EQUAL(std::string("text"), std::get
<1>(notifs
[i
++]));
1741 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION
), std::get
<0>(notifs
[i
]));
1742 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1744 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_START
), std::get
<0>(notifs
[i
]));
1745 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1747 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_END
), std::get
<0>(notifs
[i
]));
1748 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1750 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_CURSOR
), std::get
<0>(notifs
[i
]));
1751 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1753 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CURSOR_VISIBLE
), std::get
<0>(notifs
[i
]));
1754 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1756 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_FORMULA
), std::get
<0>(notifs
[i
]));
1757 CPPUNIT_ASSERT_EQUAL(std::string("blah"), std::get
<1>(notifs
[i
++]));
1759 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_SET_PART
), std::get
<0>(notifs
[i
]));
1760 CPPUNIT_ASSERT_EQUAL(std::string("1"), std::get
<1>(notifs
[i
++]));
1762 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1763 CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get
<1>(notifs
[i
++]));
1766 void DesktopLOKTest::testTileInvalidationCompression()
1768 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1770 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1771 comphelper::ScopeGuard
aGuard([]()
1773 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1776 // Single part merging
1778 std::vector
<std::tuple
<int, std::string
>> notifs
;
1779 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1780 handler
->setViewId(SfxLokHelper::getView());
1782 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0, 0"_ostr
);
1783 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0, 0"_ostr
);
1784 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -50, 500, 650, 0, 0"_ostr
);
1785 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0, 0"_ostr
);
1786 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "100, 100, 200, 200, 0, 0"_ostr
);
1788 Scheduler::ProcessEventsToIdle();
1790 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1793 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1794 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 400, 600, 0, 0"), std::get
<1>(notifs
[i
++]));
1799 std::vector
<std::tuple
<int, std::string
>> notifs
;
1800 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1801 handler
->setViewId(SfxLokHelper::getView());
1803 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0, 0"_ostr
);
1804 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1, 0"_ostr
); // Different part
1805 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, 2, 0"_ostr
); // Invalid
1806 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, 0, 0"_ostr
); // Inside first
1807 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 1, 0"_ostr
); // Invalid
1809 Scheduler::ProcessEventsToIdle();
1811 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1814 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1815 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 1, 0"), std::get
<1>(notifs
[i
++]));
1817 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1818 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 239, 239, 0, 0"), std::get
<1>(notifs
[i
++]));
1823 std::vector
<std::tuple
<int, std::string
>> notifs
;
1824 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1825 handler
->setViewId(SfxLokHelper::getView());
1827 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0, 0"_ostr
); // 0
1828 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1, 0"_ostr
); // 1: Different part
1829 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1, 0"_ostr
); // Invalid
1830 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, -1, 0"_ostr
); // 0: All parts
1831 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1, 0"_ostr
); // Invalid
1832 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -100, 1200, 1200, -1, 0"_ostr
); // 0: All parts
1833 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 3, 0"_ostr
); // Overlapped
1834 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 2, 0"_ostr
); // 1: Unique region
1836 Scheduler::ProcessEventsToIdle();
1838 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1841 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1842 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1100, 1100, -1, 0"), std::get
<1>(notifs
[i
++]));
1844 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1845 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 2, 0"), std::get
<1>(notifs
[i
++]));
1848 // All Parts (partial)
1850 std::vector
<std::tuple
<int, std::string
>> notifs
;
1851 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1852 handler
->setViewId(SfxLokHelper::getView());
1854 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 0, 0"_ostr
); // 0
1855 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 100, 100, 1, 0"_ostr
); // 1: Different part
1856 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1, 0"_ostr
); // Invalid
1857 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 50, 50, -1, 0"_ostr
); // 2: All-parts
1858 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1, 0"_ostr
); // Invalid
1859 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 40, 40, 3, 0"_ostr
); // Overlapped w/ 2
1860 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 4, 0"_ostr
); // 3: Unique
1861 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 1, 0"_ostr
); // 4: Unique
1863 Scheduler::ProcessEventsToIdle();
1865 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), notifs
.size());
1868 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1869 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 0, 0"), std::get
<1>(notifs
[i
++]));
1871 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1872 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 100, 100, 1, 0"), std::get
<1>(notifs
[i
++]));
1874 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1875 CPPUNIT_ASSERT_EQUAL(std::string("150, 150, 50, 50, -1, 0"), std::get
<1>(notifs
[i
++]));
1877 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1878 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 4, 0"), std::get
<1>(notifs
[i
++]));
1880 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1881 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 1, 0"), std::get
<1>(notifs
[i
++]));
1884 // Merge with "EMPTY"
1886 std::vector
<std::tuple
<int, std::string
>> notifs
;
1887 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1888 handler
->setViewId(SfxLokHelper::getView());
1890 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0, 0"_ostr
);
1891 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "EMPTY, 0, 0"_ostr
);
1892 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 240, 0, 0"_ostr
);
1893 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 300, 300, 0, 0"_ostr
);
1894 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0, 0"_ostr
);
1896 Scheduler::ProcessEventsToIdle();
1898 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1901 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1902 CPPUNIT_ASSERT_EQUAL(std::string("EMPTY, 0, 0"), std::get
<1>(notifs
[i
++]));
1906 void DesktopLOKTest::testPartInInvalidation()
1908 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1909 // No part in invalidation: merge.
1911 std::vector
<std::tuple
<int, std::string
>> notifs
;
1912 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1913 handler
->setViewId(SfxLokHelper::getView());
1915 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10"_ostr
);
1916 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10"_ostr
);
1918 Scheduler::ProcessEventsToIdle();
1920 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1922 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
1923 CPPUNIT_ASSERT_EQUAL(std::string("10, 10, 30, 10"), std::get
<1>(notifs
[0]));
1925 // No part in invalidation: don't merge.
1927 std::vector
<std::tuple
<int, std::string
>> notifs
;
1928 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1929 handler
->setViewId(SfxLokHelper::getView());
1931 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10"_ostr
);
1932 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "40, 10, 20, 10"_ostr
);
1934 Scheduler::ProcessEventsToIdle();
1936 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1939 // Part in invalidation, intersection and parts match -> merge.
1941 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1942 comphelper::ScopeGuard
aGuard([]()
1944 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1947 std::vector
<std::tuple
<int, std::string
>> notifs
;
1948 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1949 handler
->setViewId(SfxLokHelper::getView());
1951 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0, 0"_ostr
);
1952 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 0, 0"_ostr
);
1954 Scheduler::ProcessEventsToIdle();
1956 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1958 // Part in invalidation, intersection and parts don't match -> don't merge.
1960 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1961 comphelper::ScopeGuard
aGuard([]()
1963 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1966 std::vector
<std::tuple
<int, std::string
>> notifs
;
1967 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1968 handler
->setViewId(SfxLokHelper::getView());
1970 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0, 0"_ostr
);
1971 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 1, 0"_ostr
);
1973 Scheduler::ProcessEventsToIdle();
1975 // This failed as RectangleAndPart::Create() always assumed no part in
1976 // payload, so this was merged -> it was 1.
1977 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1981 static void callbackBinaryCallbackTest(const int type
, const char* payload
, void* data
)
1983 std::vector
<std::tuple
<int, std::string
>>* notifs
= static_cast<std::vector
<std::tuple
<int, std::string
>>*>(data
);
1984 notifs
->emplace_back(type
, std::string(payload
? payload
: "(nil)"));
1987 void DesktopLOKTest::testBinaryCallback()
1989 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1991 const tools::Rectangle
rect1(Point(10,15),Size(20,25));
1992 const std::string
rect1String(rect1
.toString());
1993 // Verify that using queue() and libreOfficeKitViewInvalidateTilesCallback() has the same result.
1995 std::vector
<std::tuple
<int, std::string
>> notifs
;
1996 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackBinaryCallbackTest
, ¬ifs
));
1997 handler
->setViewId(SfxLokHelper::getView());
1999 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, OString(rect1String
));
2001 Scheduler::ProcessEventsToIdle();
2003 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
2004 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
2005 CPPUNIT_ASSERT_EQUAL(rect1String
, std::get
<1>(notifs
[0]));
2008 std::vector
<std::tuple
<int, std::string
>> notifs
;
2009 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackBinaryCallbackTest
, ¬ifs
));
2010 handler
->setViewId(SfxLokHelper::getView());
2012 handler
->libreOfficeKitViewInvalidateTilesCallback(&rect1
, INT_MIN
, 0);
2014 Scheduler::ProcessEventsToIdle();
2016 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
2017 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
2018 CPPUNIT_ASSERT_EQUAL(rect1String
, std::get
<1>(notifs
[0]));
2020 // Verify that the "EMPTY" invalidation gets converted properly.
2022 std::vector
<std::tuple
<int, std::string
>> notifs
;
2023 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackBinaryCallbackTest
, ¬ifs
));
2024 handler
->setViewId(SfxLokHelper::getView());
2026 handler
->libreOfficeKitViewInvalidateTilesCallback(nullptr, INT_MIN
, 0);
2028 Scheduler::ProcessEventsToIdle();
2030 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
2031 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
2032 CPPUNIT_ASSERT_EQUAL(std::string("EMPTY"), std::get
<1>(notifs
[0]));
2036 void DesktopLOKTest::testInput()
2038 // Load a Writer document, enable change recording and press a key.
2039 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2041 Scheduler::ProcessEventsToIdle(); // Get focus & other bits setup.
2043 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "far");
2044 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "far");
2045 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
2046 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
2047 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "beyond");
2048 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "beyond");
2049 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
2050 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
2052 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "kovely");
2053 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "kovely");
2055 pDocument
->pClass
->removeTextContext(pDocument
, 0, 6, 0);
2056 // Replace it with lovely
2057 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "lovely");
2058 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "lovely");
2059 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
2060 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
2063 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2064 Scheduler::ProcessEventsToIdle();
2065 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2066 CPPUNIT_ASSERT(pText
!= nullptr);
2067 CPPUNIT_ASSERT_EQUAL("far beyond lovely "_ostr
, OString(pText
));
2071 void DesktopLOKTest::testRedlineWriter()
2073 // Load a Writer document, enable change recording and press a key.
2074 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2075 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
2076 xPropertySet
->setPropertyValue(u
"RecordChanges"_ustr
, uno::Any(true));
2077 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
2078 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
2079 Scheduler::ProcessEventsToIdle();
2081 // Get redline info.
2082 boost::property_tree::ptree aTree
;
2083 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
2084 std::stringstream
aStream(pJSON
);
2086 CPPUNIT_ASSERT(!aStream
.str().empty());
2087 boost::property_tree::read_json(aStream
, aTree
);
2088 // Make sure that pressing a key creates exactly one redline.
2089 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
2091 for (const boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
2092 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
2093 CPPUNIT_ASSERT_EQUAL(std::string("Insert \xE2\x80\x9Ct\xE2\x80\x9D"), rRedline
.second
.get
<std::string
>("description"));
2094 // U+201C LEFT DOUBLE QUOTATION MARK, U+201D RIGHT DOUBLE QUOTATION
2098 void DesktopLOKTest::testRedlineCalc()
2100 // Load a Writer document, enable change recording and press a key.
2101 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2102 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
2103 xPropertySet
->setPropertyValue(u
"RecordChanges"_ustr
, uno::Any(true));
2104 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
2105 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
2106 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RETURN
);
2107 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RETURN
);
2108 Scheduler::ProcessEventsToIdle();
2110 // Get redline info.
2111 boost::property_tree::ptree aTree
;
2112 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
2113 std::stringstream
aStream(pJSON
);
2115 CPPUNIT_ASSERT(!aStream
.str().empty());
2116 boost::property_tree::read_json(aStream
, aTree
);
2117 // Make sure that pressing a key creates exactly one redline.
2118 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
2120 for (const boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
2121 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
2122 CPPUNIT_ASSERT_EQUAL(std::string("Cell B4 changed from '5' to 't'"), rRedline
.second
.get
<std::string
>("description"));
2129 LibLODocument_Impl
* mpDocument
;
2132 OString m_aCellFormula
;
2133 int m_nTableSelectionCount
;
2134 int m_nColorPaletteCallbackCount
= 0;
2135 bool m_bEmptyTableSelection
;
2136 bool m_bTilesInvalidated
;
2138 tools::Rectangle m_aOwnCursor
;
2139 boost::property_tree::ptree m_aCommentCallbackResult
;
2140 boost::property_tree::ptree m_aColorPaletteCallbackResult
;
2142 ViewCallback(LibLODocument_Impl
* pDocument
)
2143 : mpDocument(pDocument
),
2144 m_nTableSelectionCount(0),
2145 m_bEmptyTableSelection(false),
2146 m_bTilesInvalidated(false),
2147 m_bZeroCursor(false)
2149 mnView
= SfxLokHelper::getView();
2150 mpDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, this);
2155 mpDocument
->m_pDocumentClass
->setView(mpDocument
, mnView
);
2156 mpDocument
->m_pDocumentClass
->registerCallback(mpDocument
, nullptr, nullptr);
2159 static void callback(int nType
, const char* pPayload
, void* pData
)
2161 static_cast<ViewCallback
*>(pData
)->callbackImpl(nType
, pPayload
);
2164 void callbackImpl(int nType
, const char* pPayload
)
2166 OString
aPayload(pPayload
);
2169 case LOK_CALLBACK_INVALIDATE_TILES
:
2171 m_bTilesInvalidated
= true;
2174 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
2176 uno::Sequence
<OUString
> aSeq
= comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload
));
2177 if (std::string_view("EMPTY") == pPayload
)
2179 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(4), aSeq
.getLength());
2180 m_aOwnCursor
.SetLeft(aSeq
[0].toInt32());
2181 m_aOwnCursor
.SetTop(aSeq
[1].toInt32());
2182 m_aOwnCursor
.setWidth(aSeq
[2].toInt32());
2183 m_aOwnCursor
.setHeight(aSeq
[3].toInt32());
2185 if (m_aOwnCursor
.Left() == 0 && m_aOwnCursor
.Top() == 0)
2186 m_bZeroCursor
= true;
2189 case LOK_CALLBACK_COMMENT
:
2191 m_aCommentCallbackResult
.clear();
2192 std::stringstream
aStream(pPayload
);
2193 boost::property_tree::read_json(aStream
, m_aCommentCallbackResult
);
2194 m_aCommentCallbackResult
= m_aCommentCallbackResult
.get_child("comment");
2198 case LOK_CALLBACK_CELL_FORMULA
:
2200 m_aCellFormula
= aPayload
;
2203 case LOK_CALLBACK_TABLE_SELECTED
:
2205 m_bEmptyTableSelection
= (std::string(pPayload
).compare("{ }") == 0);
2206 ++m_nTableSelectionCount
;
2209 case LOK_CALLBACK_COLOR_PALETTES
:
2211 m_aColorPaletteCallbackResult
.clear();
2212 std::stringstream
aStream(pPayload
);
2213 boost::property_tree::read_json(aStream
, m_aColorPaletteCallbackResult
);
2214 ++m_nColorPaletteCallbackCount
;
2223 void DesktopLOKTest::testPaintPartTile()
2225 // Load an impress doc of 2 slides.
2226 // ViewCallback aView1;
2227 // ViewCallback aView2;
2228 LibLODocument_Impl
* pDocument
= loadDoc("2slides.odp");
2229 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2230 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView1);
2231 int nView1
= pDocument
->m_pDocumentClass
->getView(pDocument
);
2233 // Create a second view.
2234 pDocument
->m_pDocumentClass
->createView(pDocument
);
2235 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2236 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView2);
2238 // Go to the second slide in the second view.
2239 pDocument
->m_pDocumentClass
->setPart(pDocument
, 1);
2241 // Switch back to the first view and start typing.
2242 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
2243 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, awt::Key::TAB
);
2244 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, awt::Key::TAB
);
2245 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2246 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2247 Scheduler::ProcessEventsToIdle();
2249 // Call paintPartTile() to paint the second part (in whichever view it finds suitable for this).
2250 unsigned char pPixels
[256 * 256 * 4];
2251 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, pPixels
, 1, 0, 256, 256, 0, 0, 256, 256);
2254 Scheduler::ProcessEventsToIdle();
2255 // aView1.m_bTilesInvalidated = false;
2256 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2257 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2258 Scheduler::ProcessEventsToIdle();
2259 // This failed: paintPartTile() (as a side-effect) ended the text edit of
2260 // the first view, so there were no invalidations.
2261 //CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
2264 void DesktopLOKTest::testPaintPartTileDifferentSchemes()
2266 Color
aDarkColor(0x1c, 0x1c, 0x1c);
2268 // Add a minimal dark scheme
2270 svtools::EditableColorConfig aColorConfig
;
2271 svtools::ColorConfigValue aValue
;
2272 aValue
.bIsVisible
= true;
2273 aValue
.nColor
= aDarkColor
;
2274 aColorConfig
.SetColorValue(svtools::DOCCOLOR
, aValue
);
2275 aColorConfig
.AddScheme(u
"Dark"_ustr
);
2278 // Add a minimal light scheme
2280 svtools::EditableColorConfig aColorConfig
;
2281 svtools::ColorConfigValue aValue
;
2282 aValue
.bIsVisible
= true;
2283 aValue
.nColor
= COL_WHITE
;
2284 aColorConfig
.SetColorValue(svtools::DOCCOLOR
, aValue
);
2285 aColorConfig
.AddScheme(u
"Light"_ustr
);
2288 // This view will default to light scheme
2289 LibLODocument_Impl
* pDocument
= loadDoc("2slides.odp");
2290 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2291 int nView1
= pDocument
->m_pDocumentClass
->getView(pDocument
);
2293 // Create a second view
2294 pDocument
->m_pDocumentClass
->createView(pDocument
);
2295 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2297 // Go to the second slide in the second view
2298 pDocument
->m_pDocumentClass
->setPart(pDocument
, 1);
2300 // Set to dark scheme
2302 uno::Sequence
<beans::PropertyValue
> aPropertyValues
= comphelper::InitPropertySequence(
2304 { "NewTheme", uno::Any(u
"Dark"_ustr
) },
2307 dispatchCommand(mxComponent
, u
".uno:ChangeTheme"_ustr
, aPropertyValues
);
2310 constexpr int nCanvasWidth
= 256;
2311 constexpr int nCanvasHeight
= 256;
2313 // Just a random pixel in the middle of the canvas
2314 constexpr int nPixelX
= 128;
2315 constexpr int nPixelY
= 128 * nCanvasWidth
;
2317 std::array
<sal_uInt8
, nCanvasWidth
* nCanvasHeight
* 4> aPixels
;
2319 // Both parts should be painted with dark scheme
2320 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, aPixels
.data(), 0, 0, nCanvasWidth
, nCanvasHeight
, 0, 0, nCanvasWidth
, nCanvasHeight
);
2321 Color
aPixel(aPixels
[nPixelX
+ nPixelY
+ 0], aPixels
[nPixelX
+ nPixelY
+ 1], aPixels
[nPixelX
+ nPixelY
+ 2]);
2322 CPPUNIT_ASSERT_EQUAL(aDarkColor
, aPixel
);
2324 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, aPixels
.data(), 0, 0, nCanvasWidth
, nCanvasHeight
, 0, 0, nCanvasWidth
, nCanvasHeight
);
2325 aPixel
= Color(aPixels
[nPixelX
+ nPixelY
+ 0], aPixels
[nPixelX
+ nPixelY
+ 1], aPixels
[nPixelX
+ nPixelY
+ 2]);
2326 CPPUNIT_ASSERT_EQUAL(aDarkColor
, aPixel
);
2328 // Switch back to first view
2329 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
2331 // Both parts should be painted with light scheme
2332 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, aPixels
.data(), 0, 0, nCanvasWidth
, nCanvasHeight
, 0, 0, nCanvasWidth
, nCanvasHeight
);
2333 aPixel
= Color(aPixels
[nPixelX
+ nPixelY
+ 0], aPixels
[nPixelX
+ nPixelY
+ 1], aPixels
[nPixelX
+ nPixelY
+ 2]);
2334 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, aPixel
);
2336 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, aPixels
.data(), 0, 0, nCanvasWidth
, nCanvasHeight
, 0, 0, nCanvasWidth
, nCanvasHeight
);
2337 aPixel
= Color(aPixels
[nPixelX
+ nPixelY
+ 0], aPixels
[nPixelX
+ nPixelY
+ 1], aPixels
[nPixelX
+ nPixelY
+ 2]);
2338 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, aPixel
);
2342 #include <rtl/uri.hxx>
2343 void DesktopLOKTest::testGetFontSubset()
2345 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2346 OUString aFontName
= rtl::Uri::encode(
2347 u
"Liberation Sans"_ustr
,
2348 rtl_UriCharClassRelSegment
,
2349 rtl_UriEncodeKeepEscapes
,
2350 RTL_TEXTENCODING_UTF8
2352 OString aCommand
= ".uno:FontSubset&name=" + OUStringToOString(aFontName
, RTL_TEXTENCODING_UTF8
);
2353 boost::property_tree::ptree aTree
;
2354 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aCommand
.getStr());
2355 std::stringstream
aStream(pJSON
);
2356 boost::property_tree::read_json(aStream
, aTree
);
2357 CPPUNIT_ASSERT( !aTree
.empty() );
2358 CPPUNIT_ASSERT_EQUAL( std::string(".uno:FontSubset"), aTree
.get_child("commandName").get_value
<std::string
>() );
2359 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
2360 CPPUNIT_ASSERT( !aValues
.empty() );
2365 void DesktopLOKTest::testCommentsWriter()
2367 // Disable tiled rendering for comments
2368 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2370 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2371 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2372 tools::Long nWidth
, nHeight
;
2373 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
2375 // Document width alongwith without sidebar comes to be < 13000
2376 CPPUNIT_ASSERT( nWidth
< 13000 );
2378 // Can we get all the comments using .uno:ViewAnnotations command ?
2379 boost::property_tree::ptree aTree
;
2380 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2381 std::stringstream
aStream(pJSON
);
2383 CPPUNIT_ASSERT(!aStream
.str().empty());
2384 boost::property_tree::read_json(aStream
, aTree
);
2385 // There are 3 comments in the document already
2386 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aTree
.get_child("comments").size());
2388 int nComment2Id
= 0;
2389 // Check if all comment fields have valid data
2390 for (const auto& rComment
: aTree
.get_child("comments"))
2392 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2393 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("author").empty());
2394 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("html").empty());
2395 // Has a valid iso 8601 date time string
2396 css::util::DateTime aDateTime
;
2397 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime"));
2398 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2400 // This comment has a marked text range
2401 if (rComment
.second
.get
<std::string
>("html") == "<div>Comment 2</div>")
2403 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("textRange").empty());
2404 nComment2Id
= rComment
.second
.get
<int>("id");
2406 // This is a reply comment
2407 else if (rComment
.second
.get
<std::string
>("html") == "<div>Reply to Comment 2</div>")
2409 CPPUNIT_ASSERT_EQUAL(nComment2Id
, rComment
.second
.get
<int>("parentId"));
2413 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2417 void DesktopLOKTest::testCommentsCalc()
2419 // Disable tiled rendering for comments
2420 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2422 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2423 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2425 // Can we get all the comments using .uno:ViewAnnotations command ?
2426 boost::property_tree::ptree aTree
;
2427 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2428 std::stringstream
aStream(pJSON
);
2430 CPPUNIT_ASSERT(!aStream
.str().empty());
2431 boost::property_tree::read_json(aStream
, aTree
);
2432 // There are 2 comments in the document already
2433 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2435 // Check if all comment fields have valid data
2437 for (const auto& rComment
: aTree
.get_child("comments"))
2443 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2444 CPPUNIT_ASSERT_EQUAL(std::string("Comment1"), rComment
.second
.get
<std::string
>("text"));
2445 CPPUNIT_ASSERT_EQUAL(std::string("6 14 6 14"), rComment
.second
.get
<std::string
>("cellRange"));
2450 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2451 CPPUNIT_ASSERT_EQUAL(std::string("Comment2"), rComment
.second
.get
<std::string
>("text"));
2452 CPPUNIT_ASSERT_EQUAL(std::string("7 17 7 17"), rComment
.second
.get
<std::string
>("cellRange"));
2460 // We checked all the comments
2461 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2463 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2467 void DesktopLOKTest::testCommentsImpress()
2469 // Disable tiled rendering for comments
2470 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2472 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
2473 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2475 // Can we get all the comments using .uno:ViewAnnotations command ?
2476 boost::property_tree::ptree aTree
;
2477 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2478 std::stringstream
aStream(pJSON
);
2480 CPPUNIT_ASSERT(!aStream
.str().empty());
2481 boost::property_tree::read_json(aStream
, aTree
);
2482 // There are 2 comments in the document already
2483 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2485 // Check if all comment fields have valid data
2487 for (const auto& rComment
: aTree
.get_child("comments"))
2493 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2494 CPPUNIT_ASSERT_EQUAL(std::string("This is comment1"), rComment
.second
.get
<std::string
>("text"));
2495 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), rComment
.second
.get
<std::string
>("author"));
2496 css::util::DateTime aDateTime
;
2497 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime"));
2498 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2503 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2504 CPPUNIT_ASSERT_EQUAL(std::string("This is comment2"), rComment
.second
.get
<std::string
>("text"));
2505 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), rComment
.second
.get
<std::string
>("author"));
2506 css::util::DateTime aDateTime
;
2507 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime"));
2508 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2516 // We checked all the comments
2517 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2519 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2522 void DesktopLOKTest::testCommentsCallbacksWriter()
2524 // Comments callback are emitted only if tiled annotations are off
2525 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2526 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2527 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2528 ViewCallback
aView1(pDocument
);
2529 pDocument
->m_pDocumentClass
->createView(pDocument
);
2530 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2531 ViewCallback
aView2(pDocument
);
2533 // Add a new comment
2534 OString
aCommandArgs("{ \"Text\": { \"type\": \"string\", \"value\": \"Additional comment\" }, \"Author\": { \"type\": \"string\", \"value\": \"LOK User1\" } }"_ostr
);
2535 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2536 Scheduler::ProcessEventsToIdle();
2538 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2539 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2540 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2541 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2543 // Reply to a comment just added
2544 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment\" } }";
2545 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2546 Scheduler::ProcessEventsToIdle();
2548 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2549 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2550 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2551 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parentId"));
2552 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parentId"));
2553 CPPUNIT_ASSERT_EQUAL(std::string("<div>Reply comment</div>"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("html"));
2554 CPPUNIT_ASSERT_EQUAL(std::string("<div>Reply comment</div>"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("html"));
2555 int nCommentId2
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2557 // Edit the previously added comment
2558 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Edited comment\" } }";
2559 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2560 Scheduler::ProcessEventsToIdle();
2562 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2563 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2564 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2565 // parent is unchanged still
2566 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parentId"));
2567 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parentId"));
2568 CPPUNIT_ASSERT_EQUAL(std::string("<div>Edited comment</div>"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("html"));
2569 CPPUNIT_ASSERT_EQUAL(std::string("<div>Edited comment</div>"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("html"));
2571 // Delete the reply comment just added
2572 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" } }";
2573 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteComment", aCommandArgs
.getStr(), false);
2574 Scheduler::ProcessEventsToIdle();
2576 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2577 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2578 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2579 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2580 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView2
.m_aCommentCallbackResult
.get
<int>("id"));
2582 // Reply to nCommentId1 again
2583 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment again\" } }";
2584 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2585 Scheduler::ProcessEventsToIdle();
2587 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2588 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2589 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2590 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parentId"));
2591 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parentId"));
2592 CPPUNIT_ASSERT_EQUAL(std::string("<div>Reply comment again</div>"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("html"));
2593 CPPUNIT_ASSERT_EQUAL(std::string("<div>Reply comment again</div>"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("html"));
2595 // .uno:ViewAnnotations returns total of 5 comments
2596 boost::property_tree::ptree aTree
;
2597 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2598 std::stringstream
aStream(pJSON
);
2600 CPPUNIT_ASSERT(!aStream
.str().empty());
2601 boost::property_tree::read_json(aStream
, aTree
);
2602 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aTree
.get_child("comments").size());
2608 void addParameter(tools::JsonWriter
& rJson
, const char* sName
, std::string_view type
, std::string_view value
)
2610 auto testNode
= rJson
.startNode(sName
);
2611 rJson
.put("type", type
);
2612 rJson
.put("value", value
);
2617 void DesktopLOKTest::testCommentsAddEditDeleteDraw()
2619 // Comments callback are emitted only if tiled annotations are off
2620 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2621 LibLODocument_Impl
* pDocument
= loadDoc("BlankDrawDocument.odg");
2622 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2623 ViewCallback
aView1(pDocument
);
2625 // Add a new comment
2626 OString aCommandArgs
;
2628 tools::JsonWriter aJson
;
2629 addParameter(aJson
, "Text", "string", "Comment");
2630 addParameter(aJson
, "Author", "string", "LOK User1");
2631 aCommandArgs
= aJson
.finishAndGetAsOString();
2634 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2635 Scheduler::ProcessEventsToIdle();
2637 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2638 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2639 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2641 // Edit the previously added comment
2643 tools::JsonWriter aJson
;
2644 addParameter(aJson
, "Id", "string", OString::number(nCommentId1
));
2645 addParameter(aJson
, "Text", "string", "Edited comment");
2646 aCommandArgs
= aJson
.finishAndGetAsOString();
2649 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2650 Scheduler::ProcessEventsToIdle();
2652 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2653 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2654 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2658 tools::JsonWriter aJson
;
2659 addParameter(aJson
, "Id", "string", OString::number(nCommentId1
));
2660 aCommandArgs
= aJson
.finishAndGetAsOString();
2662 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteAnnotation", aCommandArgs
.getStr(), false);
2663 Scheduler::ProcessEventsToIdle();
2665 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2666 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2667 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2670 void DesktopLOKTest::testCommentsInReadOnlyMode()
2672 // Comments callback are emitted only if tiled annotations are off
2673 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2674 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2676 int viewId
= pDocument
->m_pDocumentClass
->createView(pDocument
);
2677 pDocument
->m_pDocumentClass
->setView(pDocument
, viewId
);
2679 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{\".uno:Author\":{\"type\":\"string\",\"value\":\"LOK User1\"}}");
2681 SfxLokHelper::setViewReadOnly(viewId
, true);
2682 SfxLokHelper::setAllowChangeComments(viewId
, true);
2684 Scheduler::ProcessEventsToIdle();
2686 ViewCallback
aView(pDocument
);
2688 // Add a new comment
2689 OString aCommandArgs
;
2691 tools::JsonWriter aJson
;
2692 addParameter(aJson
, "Text", "string", "Comment");
2693 addParameter(aJson
, "Author", "string", "LOK User1");
2694 aCommandArgs
= aJson
.finishAndGetAsOString();
2697 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2698 Scheduler::ProcessEventsToIdle();
2700 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2701 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2702 int nCommentId
= aView
.m_aCommentCallbackResult
.get
<int>("id");
2704 // Edit the previously added comment
2706 tools::JsonWriter aJson
;
2707 addParameter(aJson
, "Id", "string", OString::number(nCommentId
));
2708 addParameter(aJson
, "Text", "string", "Edited comment");
2709 aCommandArgs
= aJson
.finishAndGetAsOString();
2712 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2713 Scheduler::ProcessEventsToIdle();
2715 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2716 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2717 CPPUNIT_ASSERT_EQUAL(nCommentId
, aView
.m_aCommentCallbackResult
.get
<int>("id"));
2721 tools::JsonWriter aJson
;
2722 addParameter(aJson
, "Id", "string", OString::number(nCommentId
));
2723 aCommandArgs
= aJson
.finishAndGetAsOString();
2725 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteAnnotation", aCommandArgs
.getStr(), false);
2726 Scheduler::ProcessEventsToIdle();
2728 // Result is not sent for delete operation for some reason. But it is sent when debugging with online.
2729 // TODO: Enable below 2 checks.
2731 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2732 //CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView.m_aCommentCallbackResult.get<std::string>("action"));
2733 //CPPUNIT_ASSERT_EQUAL(nCommentId, aView.m_aCommentCallbackResult.get<int>("id"));
2736 void DesktopLOKTest::testRunMacro()
2738 LibLibreOffice_Impl aOffice
;
2739 bool bGoodMacro
, bNonExistentMacro
;
2741 // Tools macros come pre-installed in system share/basic folder,
2742 bGoodMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, "macro:///Tools.Debug.ActivateReadOnlyFlag()");
2743 CPPUNIT_ASSERT(bGoodMacro
);
2745 bNonExistentMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, "macro:///I.Am.Not(There)");
2746 CPPUNIT_ASSERT(!bNonExistentMacro
);
2749 void DesktopLOKTest::testExtractParameter()
2751 OUString
aOptions(u
"Language=de-DE"_ustr
);
2752 OUString aValue
= extractParameter(aOptions
, u
"Language");
2753 CPPUNIT_ASSERT_EQUAL(u
"de-DE"_ustr
, aValue
);
2754 CPPUNIT_ASSERT_EQUAL(OUString(), aOptions
);
2756 aOptions
= "Language=en-US,Something";
2757 aValue
= extractParameter(aOptions
, u
"Language");
2758 CPPUNIT_ASSERT_EQUAL(u
"en-US"_ustr
, aValue
);
2759 CPPUNIT_ASSERT_EQUAL(u
"Something"_ustr
, aOptions
);
2761 aOptions
= "SomethingElse,Language=cs-CZ";
2762 aValue
= extractParameter(aOptions
, u
"Language");
2763 CPPUNIT_ASSERT_EQUAL(u
"cs-CZ"_ustr
, aValue
);
2764 CPPUNIT_ASSERT_EQUAL(u
"SomethingElse"_ustr
, aOptions
);
2766 aOptions
= "Something1,Language=hu-HU,Something2";
2767 aValue
= extractParameter(aOptions
, u
"Language");
2768 CPPUNIT_ASSERT_EQUAL(u
"hu-HU"_ustr
, aValue
);
2769 CPPUNIT_ASSERT_EQUAL(u
"Something1,Something2"_ustr
, aOptions
);
2771 aOptions
= "Something1,Something2=blah,Something3";
2772 aValue
= extractParameter(aOptions
, u
"Language");
2773 CPPUNIT_ASSERT_EQUAL(OUString(), aValue
);
2774 CPPUNIT_ASSERT_EQUAL(u
"Something1,Something2=blah,Something3"_ustr
, aOptions
);
2777 void DesktopLOKTest::readFileIntoByteVector(std::u16string_view sFilename
, std::vector
<unsigned char> & rByteVector
)
2779 rByteVector
.clear();
2780 OUString aURL
= createFileURL(sFilename
);
2781 SvFileStream
aStream(aURL
, StreamMode::READ
);
2782 rByteVector
.resize(aStream
.remainingSize());
2783 aStream
.ReadBytes(rByteVector
.data(), aStream
.remainingSize());
2786 void DesktopLOKTest::testGetSignatureState_Signed()
2788 LibLODocument_Impl
* pDocument
= loadDoc("signed.odt");
2789 Scheduler::ProcessEventsToIdle();
2790 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2791 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2794 // Already SignatureState::OK, then can't test the effect of trusting new CAs.
2798 CPPUNIT_ASSERT_EQUAL(int(4), nState
);
2800 std::vector
<unsigned char> aCertificate
;
2802 readFileIntoByteVector(u
"rootCA.der", aCertificate
);
2803 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2804 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2805 CPPUNIT_ASSERT(bResult
);
2809 readFileIntoByteVector(u
"intermediateRootCA.der", aCertificate
);
2810 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2811 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2812 CPPUNIT_ASSERT(bResult
);
2815 nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2816 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2819 void DesktopLOKTest::testGetSignatureState_NonSigned()
2821 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2822 Scheduler::ProcessEventsToIdle();
2823 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2824 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2825 CPPUNIT_ASSERT_EQUAL(int(0), nState
);
2828 #if 0 // broken with system nss on RHEL 7
2829 void DesktopLOKTest::testInsertCertificate_DER_ODT()
2831 // Load the document, save it into a temp file and load that file again
2832 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2833 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2836 pDocument
= loadDocUrl(maTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2838 Scheduler::ProcessEventsToIdle();
2839 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2840 Scheduler::ProcessEventsToIdle();
2842 std::vector
<unsigned char> aCertificate
;
2843 std::vector
<unsigned char> aPrivateKey
;
2846 readFileIntoByteVector(u
"rootCA.der", aCertificate
);
2848 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2849 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2850 CPPUNIT_ASSERT(bResult
);
2854 readFileIntoByteVector(u
"intermediateRootCA.der", aCertificate
);
2856 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2857 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2858 CPPUNIT_ASSERT(bResult
);
2862 readFileIntoByteVector(u
"certificate.der", aCertificate
);
2863 readFileIntoByteVector(u
"certificatePrivateKey.der", aPrivateKey
);
2865 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2866 aCertificate
.data(), int(aCertificate
.size()),
2867 aPrivateKey
.data(), int(aPrivateKey
.size()));
2868 CPPUNIT_ASSERT(bResult
);
2871 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2872 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2876 void DesktopLOKTest::testInsertCertificate_PEM_ODT()
2878 // Load the document, save it into a temp file and load that file again
2879 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2880 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2883 pDocument
= loadDocUrl(maTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2885 Scheduler::ProcessEventsToIdle();
2886 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2887 Scheduler::ProcessEventsToIdle();
2889 std::vector
<unsigned char> aCertificate
;
2890 std::vector
<unsigned char> aPrivateKey
;
2893 readFileIntoByteVector(u
"test-cert-chain-1.pem", aCertificate
);
2895 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2896 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2897 CPPUNIT_ASSERT(bResult
);
2901 readFileIntoByteVector(u
"test-cert-chain-2.pem", aCertificate
);
2903 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2904 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2905 CPPUNIT_ASSERT(bResult
);
2909 readFileIntoByteVector(u
"test-cert-chain-3.pem", aCertificate
);
2911 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2912 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2913 CPPUNIT_ASSERT(bResult
);
2917 readFileIntoByteVector(u
"test-cert-signing.pem", aCertificate
);
2918 readFileIntoByteVector(u
"test-PK-signing.pem", aPrivateKey
);
2920 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2921 aCertificate
.data(), int(aCertificate
.size()),
2922 aPrivateKey
.data(), int(aPrivateKey
.size()));
2923 CPPUNIT_ASSERT(bResult
);
2926 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2927 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2930 void DesktopLOKTest::testInsertCertificate_PEM_DOCX()
2932 // Load the document, save it into a temp file and load that file again
2933 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.docx");
2934 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "docx", nullptr));
2937 pDocument
= loadDocUrl(maTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2939 Scheduler::ProcessEventsToIdle();
2940 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2941 Scheduler::ProcessEventsToIdle();
2943 std::vector
<unsigned char> aCertificate
;
2944 std::vector
<unsigned char> aPrivateKey
;
2947 readFileIntoByteVector(u
"test-cert-chain-1.pem", aCertificate
);
2949 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2950 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2951 CPPUNIT_ASSERT(bResult
);
2955 readFileIntoByteVector(u
"test-cert-chain-2.pem", aCertificate
);
2957 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2958 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2959 CPPUNIT_ASSERT(bResult
);
2963 readFileIntoByteVector(u
"test-cert-chain-3.pem", aCertificate
);
2965 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2966 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2967 CPPUNIT_ASSERT(bResult
);
2971 readFileIntoByteVector(u
"test-cert-signing.pem", aCertificate
);
2972 readFileIntoByteVector(u
"test-PK-signing.pem", aPrivateKey
);
2974 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2975 aCertificate
.data(), int(aCertificate
.size()),
2976 aPrivateKey
.data(), int(aPrivateKey
.size()));
2977 CPPUNIT_ASSERT(bResult
);
2980 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2981 CPPUNIT_ASSERT_EQUAL(int(5), nState
);
2985 void DesktopLOKTest::testSignDocument_PEM_PDF()
2987 // Load the document, save it into a temp file and load that file again
2988 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2990 Scheduler::ProcessEventsToIdle();
2991 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2992 Scheduler::ProcessEventsToIdle();
2994 std::vector
<unsigned char> aCertificate
;
2995 std::vector
<unsigned char> aPrivateKey
;
2998 readFileIntoByteVector(u
"test-cert-chain-1.pem", aCertificate
);
3000 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
3001 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
3002 CPPUNIT_ASSERT(bResult
);
3006 readFileIntoByteVector(u
"test-cert-chain-2.pem", aCertificate
);
3008 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
3009 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
3010 CPPUNIT_ASSERT(bResult
);
3014 readFileIntoByteVector(u
"test-cert-chain-3.pem", aCertificate
);
3016 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
3017 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
3018 CPPUNIT_ASSERT(bResult
);
3021 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "pdf", nullptr));
3025 Scheduler::ProcessEventsToIdle();
3027 readFileIntoByteVector(u
"test-cert-signing.pem", aCertificate
);
3028 readFileIntoByteVector(u
"test-PK-signing.pem", aPrivateKey
);
3030 LibLibreOffice_Impl aOffice
;
3031 bool bResult
= aOffice
.m_pOfficeClass
->signDocument(&aOffice
, maTempFile
.GetURL().toUtf8().getStr(),
3032 aCertificate
.data(), int(aCertificate
.size()),
3033 aPrivateKey
.data(), int(aPrivateKey
.size()));
3035 CPPUNIT_ASSERT(bResult
);
3038 void DesktopLOKTest::testTextSelectionHandles()
3040 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
3041 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
3043 OString
aText("hello"_ostr
);
3044 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
3046 // select the inserted text
3047 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
3048 Scheduler::ProcessEventsToIdle();
3049 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
3050 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
3052 CPPUNIT_ASSERT_EQUAL("1418, 1418, 0, 275"_ostr
, m_aTextSelectionStart
);
3053 CPPUNIT_ASSERT_EQUAL("1897, 1418, 0, 275"_ostr
, m_aTextSelectionEnd
);
3056 m_aTextSelectionStart
= ""_ostr
;
3057 m_aTextSelectionEnd
= ""_ostr
;
3058 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, css::awt::Key::ESCAPE
);
3059 Scheduler::ProcessEventsToIdle();
3060 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
3061 CPPUNIT_ASSERT_EQUAL(static_cast<char *>(nullptr), pText
);
3063 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionStart
);
3064 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionEnd
);
3066 // select again; the positions of the selection handles have to be sent
3068 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
3069 Scheduler::ProcessEventsToIdle();
3070 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
3071 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
3073 CPPUNIT_ASSERT_EQUAL("1418, 1418, 0, 275"_ostr
, m_aTextSelectionStart
);
3074 CPPUNIT_ASSERT_EQUAL("1897, 1418, 0, 275"_ostr
, m_aTextSelectionEnd
);
3077 void DesktopLOKTest::testDialogPaste()
3079 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
3080 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
3081 Scheduler::ProcessEventsToIdle();
3083 SfxViewShell
* pViewShell
= SfxViewShell::Current();
3084 pViewShell
->GetViewFrame().GetBindings().Update();
3086 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
3087 CPPUNIT_ASSERT(pWindow
);
3089 pDocument
->pClass
->postWindow(pDocument
, pWindow
->GetLOKWindowId(), LOK_WINDOW_PASTE
,
3090 "{ \"MimeType\" : { \"type\" : \"string\", \"value\" : \"text/plain;charset=utf-8\" }, \"Data\" : { \"type\" : \"[]byte\", \"value\" : \"www.softwarelibre.org.bo\" } }");
3091 Scheduler::ProcessEventsToIdle();
3093 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
3094 CPPUNIT_ASSERT(pCtrlFocused
);
3095 CPPUNIT_ASSERT_EQUAL(WindowType::COMBOBOX
, pCtrlFocused
->GetType());
3096 CPPUNIT_ASSERT_EQUAL(u
"www.softwarelibre.org.bo"_ustr
, pCtrlFocused
->GetText());
3098 static_cast<SystemWindow
*>(pWindow
.get())->Close();
3099 Scheduler::ProcessEventsToIdle();
3102 void DesktopLOKTest::testComplexSelection()
3104 // Start with a blank text file and add contents.
3105 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
3106 static constexpr OString
aText("hello world"_ostr
);
3108 // Certainly not complex.
3109 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
3110 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionTypeAndText(pDocument
,
3111 "", nullptr, nullptr));
3114 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
3117 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
3118 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionTypeAndText(pDocument
,
3119 "", nullptr, nullptr));
3122 OUString aFileURL
= createFileURL(u
"paste.jpg");
3123 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
3124 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
3125 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
3128 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
3129 Scheduler::ProcessEventsToIdle();
3131 // Export as plain text, we should get only the text part "hello".
3132 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
3133 CPPUNIT_ASSERT(pText
!= nullptr);
3134 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
3137 // Export as rtf, we should also get the image.
3138 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/rtf", nullptr);
3139 CPPUNIT_ASSERT(pText
!= nullptr);
3140 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
3141 CPPUNIT_ASSERT(std::string(pText
).find("pict{") != std::string::npos
); // Must have the image as well.
3144 // Export as html, we should also get the image.
3145 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/html", nullptr);
3146 CPPUNIT_ASSERT(pText
!= nullptr);
3147 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
3148 CPPUNIT_ASSERT(std::string(pText
).find("<img") != std::string::npos
); // Must have the image as well.
3151 // We expect this to be complex.
3152 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_COMPLEX
), pDocument
->pClass
->getSelectionType(pDocument
));
3153 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_COMPLEX
), pDocument
->pClass
->getSelectionTypeAndText(pDocument
,
3154 "", nullptr, nullptr));
3157 void DesktopLOKTest::testCalcSaveAs()
3159 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
3160 CPPUNIT_ASSERT(pDocument
);
3162 // Enter some text, but don't commit.
3163 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'X', 0);
3164 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'X', 0);
3165 Scheduler::ProcessEventsToIdle();
3167 // Save as a new file.
3168 pDocument
->pClass
->saveAs(pDocument
, maTempFile
.GetURL().toUtf8().getStr(), "ods", nullptr);
3171 // Load the new document and verify that the in-flight changes are saved.
3172 pDocument
= loadDocUrl(maTempFile
.GetURL(), LOK_DOCTYPE_SPREADSHEET
);
3173 CPPUNIT_ASSERT(pDocument
);
3175 ViewCallback
aView(pDocument
);
3176 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3177 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView
);
3179 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RIGHT
);
3180 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RIGHT
);
3181 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_LEFT
);
3182 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_LEFT
);
3183 Scheduler::ProcessEventsToIdle();
3185 CPPUNIT_ASSERT_EQUAL("X"_ostr
, aView
.m_aCellFormula
);
3188 void DesktopLOKTest::testSpellcheckerMultiView()
3190 static constexpr OUString
aLangISO(u
"en-US"_ustr
);
3191 SvtSysLocaleOptions aSysLocaleOptions
;
3192 aSysLocaleOptions
.SetLocaleConfigString(aLangISO
);
3193 aSysLocaleOptions
.SetUILocaleConfigString(aLangISO
);
3194 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLangISO
, true));
3196 auto aSavedSettings
= Application::GetSettings();
3197 std::unique_ptr
<Resetter
> pResetter(
3198 new Resetter([&]() { Application::SetSettings(aSavedSettings
); }));
3199 AllSettings
aSettings(aSavedSettings
);
3200 aSettings
.SetLanguageTag(aLangISO
, true);
3201 Application::SetSettings(aSettings
);
3203 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
3204 pDocument
->pClass
->setViewLanguage(pDocument
, 0, "en-US"); // For spellchecking.
3205 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
3206 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
3208 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
3209 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
3210 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
3211 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, css::awt::Key::ESCAPE
);
3213 // Start spellchecking.
3214 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SpellDialog", nullptr, false);
3216 // Uncommenting this will result in a deadlock.
3217 // Because the language configuration above is not effective, and no
3218 // language is actually set, the spell-dialog finds no misspelled
3219 // words, and displays a message box, which must be dismissed to
3221 // Need to fix the language configuration issue to enable this.
3222 // Scheduler::ProcessEventsToIdle();
3224 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
3226 // Now create another view.
3227 const int nViewId
= pDocument
->m_pDocumentClass
->createView(pDocument
);
3228 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
3231 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nViewId
);
3233 // We should survive the destroyed view.
3234 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
3237 void DesktopLOKTest::testMultiDocuments()
3239 for (int i
= 0; i
< 3; i
++)
3242 std::unique_ptr
<LibLODocument_Impl
> document1
= loadDocImpl("blank_text.odt");
3243 LibLODocument_Impl
* pDocument1
= document1
.get();
3244 CPPUNIT_ASSERT_EQUAL(1, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3245 const int nDocId1
= pDocument1
->mnDocumentId
;
3247 const int nDoc1View0
= pDocument1
->m_pDocumentClass
->getView(pDocument1
);
3248 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3249 const int nDoc1View1
= pDocument1
->m_pDocumentClass
->createView(pDocument1
);
3250 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3251 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3252 CPPUNIT_ASSERT_EQUAL(2, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3254 // Validate the views of document 1.
3255 std::vector
<int> aViewIdsDoc1(2);
3256 CPPUNIT_ASSERT(pDocument1
->m_pDocumentClass
->getViewIds(pDocument1
, aViewIdsDoc1
.data(), aViewIdsDoc1
.size()));
3257 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, aViewIdsDoc1
[0]);
3258 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, aViewIdsDoc1
[1]);
3260 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3261 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3262 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View0
);
3263 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3264 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3265 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View1
);
3266 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3267 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3268 CPPUNIT_ASSERT_EQUAL(2, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3270 // Load another document.
3271 std::unique_ptr
<LibLODocument_Impl
> document2
= loadDocImpl("blank_presentation.odp");
3272 LibLODocument_Impl
* pDocument2
= document2
.get();
3273 CPPUNIT_ASSERT_EQUAL(1, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3274 const int nDocId2
= pDocument2
->mnDocumentId
;
3276 const int nDoc2View0
= pDocument2
->m_pDocumentClass
->getView(pDocument2
);
3277 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3278 const int nDoc2View1
= pDocument2
->m_pDocumentClass
->createView(pDocument2
);
3279 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3280 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3281 CPPUNIT_ASSERT_EQUAL(2, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3283 // Validate the views of document 2.
3284 std::vector
<int> aViewIdsDoc2(2);
3285 CPPUNIT_ASSERT(pDocument2
->m_pDocumentClass
->getViewIds(pDocument2
, aViewIdsDoc2
.data(), aViewIdsDoc2
.size()));
3286 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, aViewIdsDoc2
[0]);
3287 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, aViewIdsDoc2
[1]);
3289 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3290 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3291 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View0
);
3292 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3293 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3294 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View1
);
3295 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3296 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3297 CPPUNIT_ASSERT_EQUAL(2, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3299 // The views of document1 should be unchanged.
3300 CPPUNIT_ASSERT(pDocument1
->m_pDocumentClass
->getViewIds(pDocument1
, aViewIdsDoc1
.data(), aViewIdsDoc1
.size()));
3301 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, aViewIdsDoc1
[0]);
3302 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, aViewIdsDoc1
[1]);
3303 // Switch views in the first doc.
3304 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3305 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View0
);
3306 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3307 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3308 pDocument1
->m_pDocumentClass
->destroyView(pDocument1
, nDoc1View1
);
3309 CPPUNIT_ASSERT_EQUAL(1, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3311 // The views of document2 should be unchanged.
3312 CPPUNIT_ASSERT(pDocument2
->m_pDocumentClass
->getViewIds(pDocument2
, aViewIdsDoc2
.data(), aViewIdsDoc2
.size()));
3313 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, aViewIdsDoc2
[0]);
3314 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, aViewIdsDoc2
[1]);
3315 // Switch views in the second doc.
3316 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3317 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View0
);
3318 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3319 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3320 pDocument2
->m_pDocumentClass
->destroyView(pDocument2
, nDoc2View1
);
3321 CPPUNIT_ASSERT_EQUAL(1, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3323 closeDoc(document2
);
3325 closeDoc(document1
);
3329 void DesktopLOKTest::testControlState()
3331 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
3332 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:StarShapes", nullptr, false);
3333 TestLokCallbackWrapper::InitializeSidebar();
3334 Scheduler::ProcessEventsToIdle();
3336 boost::property_tree::ptree aState
;
3337 SfxViewShell
* pViewShell
= SfxViewShell::Current();
3338 pViewShell
->GetViewFrame().GetBindings().Update();
3339 pViewShell
->GetViewFrame().GetBindings().QueryControlState(SID_ATTR_TRANSFORM_WIDTH
, aState
);
3340 CPPUNIT_ASSERT(!aState
.empty());
3343 void DesktopLOKTest::testMetricField()
3345 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
3346 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:StarShapes", nullptr, false);
3347 SfxChildWindow
* pSideBar
= TestLokCallbackWrapper::InitializeSidebar();
3348 Scheduler::ProcessEventsToIdle();
3350 vcl::Window
* pWin
= pSideBar
->GetWindow();
3351 CPPUNIT_ASSERT(pWin
);
3353 WindowUIObject
aWinUI(pWin
);
3354 std::unique_ptr
<UIObject
> pUIWin(aWinUI
.get_child(u
"selectwidth"_ustr
));
3355 CPPUNIT_ASSERT(pUIWin
);
3358 aMap
[u
"VALUE"_ustr
] = "75.06";
3359 pUIWin
->execute(u
"VALUE"_ustr
, aMap
);
3361 StringMap aRet
= pUIWin
->get_state();
3362 CPPUNIT_ASSERT_EQUAL(aMap
[u
"VALUE"_ustr
], aRet
[u
"Value"_ustr
]);
3365 void DesktopLOKTest::testJumpCursor()
3367 comphelper::LibreOfficeKit::setTiledAnnotations(false);
3369 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
3370 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3372 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'B', 0);
3373 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'o', 0);
3374 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'l', 0);
3375 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'i', 0);
3376 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'v', 0);
3377 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'i', 0);
3378 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
3379 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, css::awt::Key::ESCAPE
);
3380 Scheduler::ProcessEventsToIdle();
3382 // There is a cursor jump to (0, 0) due to
3383 // mpOutlinerView->SetOutputArea( PixelToLogic( tools::Rectangle(0,0,1,1) ) );
3384 // when creating a comment
3385 ViewCallback
aView1(pDocument
);
3387 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", nullptr, true);
3388 Scheduler::ProcessEventsToIdle();
3390 CPPUNIT_ASSERT(!aView1
.m_bZeroCursor
);
3392 comphelper::LibreOfficeKit::setTiledAnnotations(true);
3395 void DesktopLOKTest::testRenderSearchResult_WriterNode()
3397 constexpr const bool bDumpBitmap
= false;
3399 LibLODocument_Impl
* pDocument
= loadDoc("SearchIndexResultTest.odt");
3400 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3402 Scheduler::ProcessEventsToIdle();
3404 unsigned char* pBuffer
= nullptr;
3407 "<paragraph node_type=\"writer\" index=\"19\">ABC</paragraph>"
3412 size_t nByteSize
= 0;
3414 bool bResult
= pDocument
->m_pDocumentClass
->renderSearchResult(pDocument
, aPayload
.getStr(), &pBuffer
, &nWidth
, &nHeight
, &nByteSize
);
3416 CPPUNIT_ASSERT(bResult
);
3417 CPPUNIT_ASSERT(pBuffer
);
3419 Scheduler::ProcessEventsToIdle();
3421 CPPUNIT_ASSERT_EQUAL(642, nWidth
);
3422 CPPUNIT_ASSERT_EQUAL(561, nHeight
);
3423 CPPUNIT_ASSERT_EQUAL(size_t(1440648), nByteSize
);
3425 const sal_uInt8
* pD
= reinterpret_cast<const sal_uInt8
*>(pBuffer
);
3426 BitmapEx aBitmap
= vcl::bitmap::CreateFromData(pD
, nWidth
, nHeight
, nWidth
* 4, /*nBitsPerPixel*/32, true, true);
3430 SvFileStream
aStream(u
"~/SearchResultBitmap.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
3431 vcl::PngImageWriter
aPNGWriter(aStream
);
3432 aPNGWriter
.write(aBitmap
);
3434 CPPUNIT_ASSERT_EQUAL(tools::Long(642), aBitmap
.GetSizePixel().Width());
3435 CPPUNIT_ASSERT_EQUAL(tools::Long(561), aBitmap
.GetSizePixel().Height());
3440 void DesktopLOKTest::testRenderSearchResult_CommonNode()
3442 constexpr const bool bDumpBitmap
= false;
3444 LibLODocument_Impl
* pDocument
= loadDoc("SearchIndexResultShapeTest.odt");
3445 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3447 Scheduler::ProcessEventsToIdle();
3449 unsigned char* pBuffer
= nullptr;
3452 "<paragraph node_type=\"common\" index=\"0\" object_name=\"Shape 1\" />"
3457 size_t nByteSize
= 0;
3459 bool bResult
= pDocument
->m_pDocumentClass
->renderSearchResult(pDocument
, aPayload
.getStr(), &pBuffer
, &nWidth
, &nHeight
, &nByteSize
);
3461 CPPUNIT_ASSERT(bResult
);
3462 CPPUNIT_ASSERT(pBuffer
);
3464 Scheduler::ProcessEventsToIdle();
3466 CPPUNIT_ASSERT_EQUAL(192, nWidth
);
3467 CPPUNIT_ASSERT_EQUAL(96, nHeight
);
3468 CPPUNIT_ASSERT_EQUAL(size_t(73728), nByteSize
);
3470 const sal_uInt8
* pD
= reinterpret_cast<const sal_uInt8
*>(pBuffer
);
3471 BitmapEx aBitmap
= vcl::bitmap::CreateFromData(pD
, nWidth
, nHeight
, nWidth
* 4, /*nBitsPerPixel*/32, true, true);
3475 SvFileStream
aStream(u
"~/SearchResultBitmap.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
3476 vcl::PngImageWriter
aPNGWriter(aStream
);
3477 aPNGWriter
.write(aBitmap
);
3479 CPPUNIT_ASSERT_EQUAL(tools::Long(192), aBitmap
.GetSizePixel().Width());
3480 CPPUNIT_ASSERT_EQUAL(tools::Long(96), aBitmap
.GetSizePixel().Height());
3485 static void lcl_repeatKeyStroke(LibLODocument_Impl
*pDocument
, int nCharCode
, int nKeyCode
, size_t nCount
)
3487 for (size_t nCtr
= 0; nCtr
< nCount
; ++nCtr
)
3489 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, nCharCode
, nKeyCode
);
3490 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, nCharCode
, nKeyCode
);
3494 void DesktopLOKTest::testNoDuplicateTableSelection()
3496 LibLODocument_Impl
* pDocument
= loadDoc("table-selection.odt");
3499 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3500 ViewCallback
aView1(pDocument
);
3502 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 1);
3503 Scheduler::ProcessEventsToIdle();
3504 CPPUNIT_ASSERT_EQUAL(1, aView1
.m_nTableSelectionCount
);
3505 CPPUNIT_ASSERT(aView1
.m_bEmptyTableSelection
);
3507 aView1
.m_nTableSelectionCount
= 0;
3509 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 1);
3510 Scheduler::ProcessEventsToIdle();
3511 CPPUNIT_ASSERT_EQUAL(1, aView1
.m_nTableSelectionCount
);
3512 CPPUNIT_ASSERT(!aView1
.m_bEmptyTableSelection
);
3514 aView1
.m_nTableSelectionCount
= 0;
3515 // Move to the last row in Table1.
3516 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 2);
3517 Scheduler::ProcessEventsToIdle();
3518 CPPUNIT_ASSERT_EQUAL(0, aView1
.m_nTableSelectionCount
);
3520 // Go outside Table1.
3521 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 1);
3522 Scheduler::ProcessEventsToIdle();
3523 CPPUNIT_ASSERT_EQUAL(1, aView1
.m_nTableSelectionCount
);
3524 CPPUNIT_ASSERT(aView1
.m_bEmptyTableSelection
);
3527 void DesktopLOKTest::testMultiViewTableSelection()
3529 LibLODocument_Impl
* pDocument
= loadDoc("table-selection.odt");
3532 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3533 ViewCallback
aView1(pDocument
);
3534 int nView1
= pDocument
->m_pDocumentClass
->getView(pDocument
);
3537 pDocument
->m_pDocumentClass
->createView(pDocument
);
3538 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3539 ViewCallback
aView2(pDocument
);
3540 int nView2
= pDocument
->m_pDocumentClass
->getView(pDocument
);
3542 // switch to view 1.
3543 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
3544 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 1);
3545 Scheduler::ProcessEventsToIdle();
3546 CPPUNIT_ASSERT_EQUAL(1, aView1
.m_nTableSelectionCount
);
3547 CPPUNIT_ASSERT_EQUAL(1, aView2
.m_nTableSelectionCount
);
3548 CPPUNIT_ASSERT(aView1
.m_bEmptyTableSelection
);
3549 CPPUNIT_ASSERT(aView2
.m_bEmptyTableSelection
);
3551 aView1
.m_nTableSelectionCount
= 0;
3552 aView2
.m_nTableSelectionCount
= 0;
3554 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
3556 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 1);
3557 Scheduler::ProcessEventsToIdle();
3558 CPPUNIT_ASSERT_EQUAL(1, aView1
.m_nTableSelectionCount
);
3559 CPPUNIT_ASSERT_EQUAL(0, aView2
.m_nTableSelectionCount
);
3561 aView1
.m_nTableSelectionCount
= 0;
3563 pDocument
->m_pDocumentClass
->setView(pDocument
, nView2
);
3564 // Go to Table2 in view 2.
3565 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 7);
3566 Scheduler::ProcessEventsToIdle();
3567 // View1 should not get any table selection messages.
3568 CPPUNIT_ASSERT_EQUAL(0, aView1
.m_nTableSelectionCount
);
3569 // View2 will first get table selection of Table1, then empty selection, and finally on 7th down arrow keypress,
3570 // it will get table-selection of Table2. So in total it should get 3 table selections.
3571 CPPUNIT_ASSERT_EQUAL(3, aView2
.m_nTableSelectionCount
);
3572 CPPUNIT_ASSERT(!aView2
.m_bEmptyTableSelection
);
3574 aView1
.m_nTableSelectionCount
= 0;
3575 aView2
.m_nTableSelectionCount
= 0;
3578 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
3579 // Go out of Table1 and re-enter..
3580 lcl_repeatKeyStroke(pDocument
, 0, KEY_UP
, 1);
3581 lcl_repeatKeyStroke(pDocument
, 0, KEY_DOWN
, 1);
3582 Scheduler::ProcessEventsToIdle();
3583 // View1 should get one empty table selection, then get Table1 selection.
3584 CPPUNIT_ASSERT_EQUAL(2, aView1
.m_nTableSelectionCount
);
3585 // View2 should not get any table selection.
3586 CPPUNIT_ASSERT_EQUAL(0, aView2
.m_nTableSelectionCount
);
3587 CPPUNIT_ASSERT(!aView1
.m_bEmptyTableSelection
);
3590 void DesktopLOKTest::testColorPaletteCallback()
3592 LibLODocument_Impl
* pDocument
= loadDoc("ThemeDocument.docx");
3595 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3596 ViewCallback
aView1(pDocument
);
3597 Scheduler::ProcessEventsToIdle();
3599 CPPUNIT_ASSERT_EQUAL(1, aView1
.m_nColorPaletteCallbackCount
);
3600 boost::property_tree::ptree aValues
= aView1
.m_aColorPaletteCallbackResult
.get_child("ThemeColors");
3601 CPPUNIT_ASSERT(!aValues
.empty());
3602 CPPUNIT_ASSERT_EQUAL(size_t(6), aValues
.size());
3606 pDocument
->m_pDocumentClass
->createView(pDocument
);
3607 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3608 ViewCallback
aView2(pDocument
);
3609 Scheduler::ProcessEventsToIdle();
3611 CPPUNIT_ASSERT_EQUAL(1, aView2
.m_nColorPaletteCallbackCount
);
3612 boost::property_tree::ptree aValues
= aView1
.m_aColorPaletteCallbackResult
.get_child("ThemeColors");
3613 CPPUNIT_ASSERT(!aValues
.empty());
3614 CPPUNIT_ASSERT_EQUAL(size_t(6), aValues
.size());
3620 constexpr size_t classOffset(int i
)
3622 return sizeof(static_cast<struct _LibreOfficeKitClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
3625 constexpr size_t documentClassOffset(int i
)
3627 return sizeof(static_cast<struct _LibreOfficeKitDocumentClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
3632 void DesktopLOKTest::testABI()
3634 // STABLE ABI, NEVER CHANGE (unless there's a very good reason, agreed by ESC, etc.)
3635 CPPUNIT_ASSERT_EQUAL(classOffset(0), offsetof(struct _LibreOfficeKitClass
, destroy
));
3636 CPPUNIT_ASSERT_EQUAL(classOffset(1), offsetof(struct _LibreOfficeKitClass
, documentLoad
));
3637 CPPUNIT_ASSERT_EQUAL(classOffset(2), offsetof(struct _LibreOfficeKitClass
, getError
));
3638 CPPUNIT_ASSERT_EQUAL(classOffset(3), offsetof(struct _LibreOfficeKitClass
, documentLoadWithOptions
));
3639 CPPUNIT_ASSERT_EQUAL(classOffset(4), offsetof(struct _LibreOfficeKitClass
, freeError
));
3640 CPPUNIT_ASSERT_EQUAL(classOffset(5), offsetof(struct _LibreOfficeKitClass
, registerCallback
));
3641 CPPUNIT_ASSERT_EQUAL(classOffset(6), offsetof(struct _LibreOfficeKitClass
, getFilterTypes
));
3642 CPPUNIT_ASSERT_EQUAL(classOffset(7), offsetof(struct _LibreOfficeKitClass
, setOptionalFeatures
));
3643 CPPUNIT_ASSERT_EQUAL(classOffset(8), offsetof(struct _LibreOfficeKitClass
, setDocumentPassword
));
3644 CPPUNIT_ASSERT_EQUAL(classOffset(9), offsetof(struct _LibreOfficeKitClass
, getVersionInfo
));
3645 CPPUNIT_ASSERT_EQUAL(classOffset(10), offsetof(struct _LibreOfficeKitClass
, runMacro
));
3646 CPPUNIT_ASSERT_EQUAL(classOffset(11), offsetof(struct _LibreOfficeKitClass
, signDocument
));
3647 CPPUNIT_ASSERT_EQUAL(classOffset(12), offsetof(struct _LibreOfficeKitClass
, runLoop
));
3648 CPPUNIT_ASSERT_EQUAL(classOffset(13), offsetof(struct _LibreOfficeKitClass
, sendDialogEvent
));
3649 CPPUNIT_ASSERT_EQUAL(classOffset(14), offsetof(struct _LibreOfficeKitClass
, setOption
));
3650 CPPUNIT_ASSERT_EQUAL(classOffset(15), offsetof(struct _LibreOfficeKitClass
, dumpState
));
3651 CPPUNIT_ASSERT_EQUAL(classOffset(16), offsetof(struct _LibreOfficeKitClass
, extractRequest
));
3652 CPPUNIT_ASSERT_EQUAL(classOffset(17), offsetof(struct _LibreOfficeKitClass
, trimMemory
));
3653 CPPUNIT_ASSERT_EQUAL(classOffset(18), offsetof(struct _LibreOfficeKitClass
, startURP
));
3654 CPPUNIT_ASSERT_EQUAL(classOffset(19), offsetof(struct _LibreOfficeKitClass
, stopURP
));
3655 CPPUNIT_ASSERT_EQUAL(classOffset(20), offsetof(struct _LibreOfficeKitClass
, joinThreads
));
3656 CPPUNIT_ASSERT_EQUAL(classOffset(21), offsetof(struct _LibreOfficeKitClass
, startThreads
));
3657 CPPUNIT_ASSERT_EQUAL(classOffset(22), offsetof(struct _LibreOfficeKitClass
, setForkedChild
));
3658 CPPUNIT_ASSERT_EQUAL(classOffset(23), offsetof(struct _LibreOfficeKitClass
, extractDocumentStructureRequest
));
3659 CPPUNIT_ASSERT_EQUAL(classOffset(24), offsetof(struct _LibreOfficeKitClass
, registerAnyInputCallback
));
3661 // When extending LibreOfficeKit with a new function pointer, add new assert for the offsetof the
3662 // new function pointer and bump this assert for the size of the class.
3663 CPPUNIT_ASSERT_EQUAL(classOffset(25), sizeof(struct _LibreOfficeKitClass
));
3665 CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass
, destroy
));
3666 CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass
, saveAs
));
3668 // Unstable ABI, but still think twice before changing this
3669 // Eg. can't you add your new member at the end of the struct instead of
3670 // in the middle? The thing you are changing - is it already part of some
3672 CPPUNIT_ASSERT_EQUAL(documentClassOffset(2), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentType
));
3673 CPPUNIT_ASSERT_EQUAL(documentClassOffset(3), offsetof(struct _LibreOfficeKitDocumentClass
, getParts
));
3674 CPPUNIT_ASSERT_EQUAL(documentClassOffset(4), offsetof(struct _LibreOfficeKitDocumentClass
, getPartPageRectangles
));
3675 CPPUNIT_ASSERT_EQUAL(documentClassOffset(5), offsetof(struct _LibreOfficeKitDocumentClass
, getPart
));
3676 CPPUNIT_ASSERT_EQUAL(documentClassOffset(6), offsetof(struct _LibreOfficeKitDocumentClass
, setPart
));
3677 CPPUNIT_ASSERT_EQUAL(documentClassOffset(7), offsetof(struct _LibreOfficeKitDocumentClass
, getPartName
));
3678 CPPUNIT_ASSERT_EQUAL(documentClassOffset(8), offsetof(struct _LibreOfficeKitDocumentClass
, setPartMode
));
3679 CPPUNIT_ASSERT_EQUAL(documentClassOffset(9), offsetof(struct _LibreOfficeKitDocumentClass
, paintTile
));
3680 CPPUNIT_ASSERT_EQUAL(documentClassOffset(10), offsetof(struct _LibreOfficeKitDocumentClass
, getTileMode
));
3681 CPPUNIT_ASSERT_EQUAL(documentClassOffset(11), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentSize
));
3682 CPPUNIT_ASSERT_EQUAL(documentClassOffset(12), offsetof(struct _LibreOfficeKitDocumentClass
, initializeForRendering
));
3683 CPPUNIT_ASSERT_EQUAL(documentClassOffset(13), offsetof(struct _LibreOfficeKitDocumentClass
, registerCallback
));
3684 CPPUNIT_ASSERT_EQUAL(documentClassOffset(14), offsetof(struct _LibreOfficeKitDocumentClass
, postKeyEvent
));
3685 CPPUNIT_ASSERT_EQUAL(documentClassOffset(15), offsetof(struct _LibreOfficeKitDocumentClass
, postMouseEvent
));
3686 CPPUNIT_ASSERT_EQUAL(documentClassOffset(16), offsetof(struct _LibreOfficeKitDocumentClass
, postUnoCommand
));
3687 CPPUNIT_ASSERT_EQUAL(documentClassOffset(17), offsetof(struct _LibreOfficeKitDocumentClass
, setTextSelection
));
3688 CPPUNIT_ASSERT_EQUAL(documentClassOffset(18), offsetof(struct _LibreOfficeKitDocumentClass
, getTextSelection
));
3689 CPPUNIT_ASSERT_EQUAL(documentClassOffset(19), offsetof(struct _LibreOfficeKitDocumentClass
, paste
));
3690 CPPUNIT_ASSERT_EQUAL(documentClassOffset(20), offsetof(struct _LibreOfficeKitDocumentClass
, setGraphicSelection
));
3691 CPPUNIT_ASSERT_EQUAL(documentClassOffset(21), offsetof(struct _LibreOfficeKitDocumentClass
, resetSelection
));
3692 CPPUNIT_ASSERT_EQUAL(documentClassOffset(22), offsetof(struct _LibreOfficeKitDocumentClass
, getCommandValues
));
3693 CPPUNIT_ASSERT_EQUAL(documentClassOffset(23), offsetof(struct _LibreOfficeKitDocumentClass
, setClientZoom
));
3694 CPPUNIT_ASSERT_EQUAL(documentClassOffset(24), offsetof(struct _LibreOfficeKitDocumentClass
, setClientVisibleArea
));
3695 CPPUNIT_ASSERT_EQUAL(documentClassOffset(25), offsetof(struct _LibreOfficeKitDocumentClass
, createView
));
3696 CPPUNIT_ASSERT_EQUAL(documentClassOffset(26), offsetof(struct _LibreOfficeKitDocumentClass
, destroyView
));
3697 CPPUNIT_ASSERT_EQUAL(documentClassOffset(27), offsetof(struct _LibreOfficeKitDocumentClass
, setView
));
3698 CPPUNIT_ASSERT_EQUAL(documentClassOffset(28), offsetof(struct _LibreOfficeKitDocumentClass
, getView
));
3699 CPPUNIT_ASSERT_EQUAL(documentClassOffset(29), offsetof(struct _LibreOfficeKitDocumentClass
, getViewsCount
));
3700 CPPUNIT_ASSERT_EQUAL(documentClassOffset(30), offsetof(struct _LibreOfficeKitDocumentClass
, renderFont
));
3701 CPPUNIT_ASSERT_EQUAL(documentClassOffset(31), offsetof(struct _LibreOfficeKitDocumentClass
, getPartHash
));
3702 CPPUNIT_ASSERT_EQUAL(documentClassOffset(32), offsetof(struct _LibreOfficeKitDocumentClass
, paintPartTile
));
3703 CPPUNIT_ASSERT_EQUAL(documentClassOffset(33), offsetof(struct _LibreOfficeKitDocumentClass
, getViewIds
));
3704 CPPUNIT_ASSERT_EQUAL(documentClassOffset(34), offsetof(struct _LibreOfficeKitDocumentClass
, setOutlineState
));
3705 CPPUNIT_ASSERT_EQUAL(documentClassOffset(35), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindow
));
3706 CPPUNIT_ASSERT_EQUAL(documentClassOffset(36), offsetof(struct _LibreOfficeKitDocumentClass
, postWindow
));
3707 CPPUNIT_ASSERT_EQUAL(documentClassOffset(37), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowKeyEvent
));
3708 CPPUNIT_ASSERT_EQUAL(documentClassOffset(38), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowMouseEvent
));
3709 CPPUNIT_ASSERT_EQUAL(documentClassOffset(39), offsetof(struct _LibreOfficeKitDocumentClass
, setViewLanguage
));
3710 CPPUNIT_ASSERT_EQUAL(documentClassOffset(40), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowExtTextInputEvent
));
3711 CPPUNIT_ASSERT_EQUAL(documentClassOffset(41), offsetof(struct _LibreOfficeKitDocumentClass
, getPartInfo
));
3712 CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowDPI
));
3713 CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass
, insertCertificate
));
3714 CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), offsetof(struct _LibreOfficeKitDocumentClass
, addCertificate
));
3715 CPPUNIT_ASSERT_EQUAL(documentClassOffset(45), offsetof(struct _LibreOfficeKitDocumentClass
, getSignatureState
));
3716 CPPUNIT_ASSERT_EQUAL(documentClassOffset(46), offsetof(struct _LibreOfficeKitDocumentClass
, renderShapeSelection
));
3717 CPPUNIT_ASSERT_EQUAL(documentClassOffset(47), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowGestureEvent
));
3718 CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass
, createViewWithOptions
));
3719 CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass
, selectPart
));
3720 CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass
, moveSelectedParts
));
3721 CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass
, resizeWindow
));
3722 CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass
, getClipboard
));
3723 CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass
, setClipboard
));
3724 CPPUNIT_ASSERT_EQUAL(documentClassOffset(54), offsetof(struct _LibreOfficeKitDocumentClass
, getSelectionType
));
3725 CPPUNIT_ASSERT_EQUAL(documentClassOffset(55), offsetof(struct _LibreOfficeKitDocumentClass
, removeTextContext
));
3726 CPPUNIT_ASSERT_EQUAL(documentClassOffset(56), offsetof(struct _LibreOfficeKitDocumentClass
, sendDialogEvent
));
3727 CPPUNIT_ASSERT_EQUAL(documentClassOffset(57), offsetof(struct _LibreOfficeKitDocumentClass
, renderFontOrientation
));
3728 CPPUNIT_ASSERT_EQUAL(documentClassOffset(58), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowForView
));
3729 CPPUNIT_ASSERT_EQUAL(documentClassOffset(59), offsetof(struct _LibreOfficeKitDocumentClass
, completeFunction
));
3730 CPPUNIT_ASSERT_EQUAL(documentClassOffset(60), offsetof(struct _LibreOfficeKitDocumentClass
, setWindowTextSelection
));
3731 CPPUNIT_ASSERT_EQUAL(documentClassOffset(61), offsetof(struct _LibreOfficeKitDocumentClass
, sendFormFieldEvent
));
3732 CPPUNIT_ASSERT_EQUAL(documentClassOffset(62), offsetof(struct _LibreOfficeKitDocumentClass
, setBlockedCommandList
));
3733 CPPUNIT_ASSERT_EQUAL(documentClassOffset(63), offsetof(struct _LibreOfficeKitDocumentClass
, renderSearchResult
));
3734 CPPUNIT_ASSERT_EQUAL(documentClassOffset(64), offsetof(struct _LibreOfficeKitDocumentClass
, sendContentControlEvent
));
3735 CPPUNIT_ASSERT_EQUAL(documentClassOffset(65), offsetof(struct _LibreOfficeKitDocumentClass
, getSelectionTypeAndText
));
3736 CPPUNIT_ASSERT_EQUAL(documentClassOffset(66), offsetof(struct _LibreOfficeKitDocumentClass
, getDataArea
));
3737 CPPUNIT_ASSERT_EQUAL(documentClassOffset(67), offsetof(struct _LibreOfficeKitDocumentClass
, getEditMode
));
3738 CPPUNIT_ASSERT_EQUAL(documentClassOffset(68), offsetof(struct _LibreOfficeKitDocumentClass
, setViewTimezone
));
3739 CPPUNIT_ASSERT_EQUAL(documentClassOffset(69), offsetof(struct _LibreOfficeKitDocumentClass
, setAccessibilityState
));
3740 CPPUNIT_ASSERT_EQUAL(documentClassOffset(70), offsetof(struct _LibreOfficeKitDocumentClass
, getA11yFocusedParagraph
));
3741 CPPUNIT_ASSERT_EQUAL(documentClassOffset(71), offsetof(struct _LibreOfficeKitDocumentClass
, getA11yCaretPosition
));
3742 CPPUNIT_ASSERT_EQUAL(documentClassOffset(72), offsetof(struct _LibreOfficeKitDocumentClass
, setViewReadOnly
));
3743 CPPUNIT_ASSERT_EQUAL(documentClassOffset(73), offsetof(struct _LibreOfficeKitDocumentClass
, setAllowChangeComments
));
3744 CPPUNIT_ASSERT_EQUAL(documentClassOffset(74), offsetof(struct _LibreOfficeKitDocumentClass
, getPresentationInfo
));
3745 CPPUNIT_ASSERT_EQUAL(documentClassOffset(75), offsetof(struct _LibreOfficeKitDocumentClass
, createSlideRenderer
));
3746 CPPUNIT_ASSERT_EQUAL(documentClassOffset(76), offsetof(struct _LibreOfficeKitDocumentClass
, postSlideshowCleanup
));
3747 CPPUNIT_ASSERT_EQUAL(documentClassOffset(77), offsetof(struct _LibreOfficeKitDocumentClass
, renderNextSlideLayer
));
3748 CPPUNIT_ASSERT_EQUAL(documentClassOffset(78), offsetof(struct _LibreOfficeKitDocumentClass
, setViewOption
));
3751 CPPUNIT_ASSERT_EQUAL(documentClassOffset(79), sizeof(struct _LibreOfficeKitDocumentClass
));
3754 CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest
);
3756 CPPUNIT_PLUGIN_IMPLEMENT();
3758 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */