1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <com/sun/star/frame/Desktop.hpp>
12 #include <com/sun/star/lang/XComponent.hpp>
13 #include <com/sun/star/text/XTextDocument.hpp>
14 #include <com/sun/star/awt/Key.hpp>
15 #include <com/sun/star/awt/XReschedule.hpp>
16 #include <com/sun/star/awt/Toolkit.hpp>
17 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
18 #include <com/sun/star/util/XModifiable.hpp>
19 #include <com/sun/star/text/TextContentAnchorType.hpp>
20 #include <boost/property_tree/json_parser.hpp>
21 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/frame/XStorable.hpp>
25 #include <vcl/combobox.hxx>
26 #include <vcl/scheduler.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/syswin.hxx>
29 #include <vcl/window.hxx>
30 #include <vcl/uitest/uiobject.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <rtl/uri.hxx>
33 #include <sfx2/app.hxx>
34 #include <sfx2/objsh.hxx>
35 #include <sfx2/app.hxx>
36 #include <sfx2/childwin.hxx>
37 #include <sfx2/lokhelper.hxx>
38 #include <test/unoapi_test.hxx>
39 #include <comphelper/lok.hxx>
40 #include <comphelper/dispatchcommand.hxx>
41 #include <comphelper/propertysequence.hxx>
42 #include <osl/conditn.hxx>
43 #include <osl/thread.hxx>
44 #include <svl/srchitem.hxx>
45 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
46 #include <unotools/tempfile.hxx>
47 #include <sfx2/viewsh.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <sfx2/bindings.hxx>
50 #include <unotools/datetime.hxx>
51 #include <unotools/syslocaleoptions.hxx>
52 #include <comphelper/string.hxx>
53 #include <comphelper/scopeguard.hxx>
56 #include <config_features.h>
57 #include <config_mpl.h>
58 #include <tools/json_writer.hxx>
60 #include <lib/init.hxx>
61 #include <svx/svxids.hrc>
63 #include <cppunit/TestAssert.h>
65 using namespace com::sun::star
;
66 using namespace desktop
;
68 static LibreOfficeKitDocumentType
getDocumentTypeFromName(const char* pName
)
70 CPPUNIT_ASSERT_MESSAGE("Document name must be valid.", pName
!= nullptr);
72 const std::string
name(pName
);
73 CPPUNIT_ASSERT_MESSAGE("Document name must include extension.", name
.size() > 4);
75 const auto it
= name
.rfind('.');
78 const std::string ext
= name
.substr(it
);
81 return LOK_DOCTYPE_SPREADSHEET
;
84 return LOK_DOCTYPE_PRESENTATION
;
87 CPPUNIT_ASSERT_MESSAGE("Document name must include extension.", it
!= name
.npos
);
88 return LOK_DOCTYPE_TEXT
;
91 class DesktopLOKTest
: public UnoApiTest
96 std::function
<void ()> m_Func
;
99 Resetter(std::function
<void ()> const& rFunc
)
109 catch (...) // has to be reliable
111 fprintf(stderr
, "resetter failed with exception\n");
118 DesktopLOKTest() : UnoApiTest("/desktop/qa/data/"),
119 m_nSelectionBeforeSearchResult(0),
120 m_nSelectionAfterSearchResult(0),
126 void readFileIntoByteVector(OUString
const & sFilename
, std::vector
<sal_uInt8
> & rByteVector
);
128 virtual void setUp() override
130 comphelper::LibreOfficeKit::setActive(true);
133 mxDesktop
.set(frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
134 SfxApplication::GetOrCreate();
137 virtual void tearDown() override
141 UnoApiTest::tearDown();
143 comphelper::LibreOfficeKit::setActive(false);
146 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
147 loadDocImpl(const char* pName
, LibreOfficeKitDocumentType eType
);
149 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
150 loadDocImpl(const char* pName
)
152 return loadDocImpl(pName
, getDocumentTypeFromName(pName
));
155 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
156 loadDocUrlImpl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
158 LibLODocument_Impl
* loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
159 LibLODocument_Impl
* loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
);
160 LibLODocument_Impl
* loadDoc(const char* pName
)
162 return loadDoc(pName
, getDocumentTypeFromName(pName
));
165 void closeDoc(std::unique_ptr
<LibLODocument_Impl
>& loDocument
,
166 uno::Reference
<lang::XComponent
>& xComponent
);
167 void closeDoc() { closeDoc(m_pDocument
, mxComponent
); }
168 static void callback(int nType
, const char* pPayload
, void* pData
);
169 void callbackImpl(int nType
, const char* pPayload
);
171 void testGetStyles();
173 void testCreateView();
174 void testGetFilterTypes();
175 void testGetPartPageRectangles();
176 void testSearchCalc();
177 void testSearchAllNotificationsCalc();
178 void testPaintTile();
180 void testSaveAsCalc();
181 void testPasteWriter();
182 void testPasteWriterJPEG();
183 void testUndoWriter();
184 void testRowColumnHeaders();
185 void testHiddenRowHeaders();
186 void testCellCursor();
187 void testCommandResult();
188 void testWriterComments();
189 void testSheetOperations();
190 void testSheetSelections();
191 void testContextMenuCalc();
192 void testContextMenuWriter();
193 void testContextMenuImpress();
194 void testNotificationCompression();
195 void testTileInvalidationCompression();
196 void testPartInInvalidation();
198 void testRedlineWriter();
199 void testTrackChanges();
200 void testRedlineCalc();
201 void testPaintPartTile();
202 void testWriterCommentInsertCursor();
204 void testGetFontSubset();
206 void testCommentsWriter();
207 void testCommentsCalc();
208 void testCommentsImpress();
209 void testCommentsCallbacksWriter();
210 void testCommentsAddEditDeleteDraw();
212 void testExtractParameter();
213 void testGetSignatureState_NonSigned();
214 void testGetSignatureState_Signed();
215 void testInsertCertificate_DER_ODT();
216 void testInsertCertificate_PEM_ODT();
217 void testInsertCertificate_PEM_DOCX();
218 void testSignDocument_PEM_PDF();
219 void testTextSelectionHandles();
220 void testComplexSelection();
221 void testSpellcheckerMultiView();
222 void testDialogPaste();
223 void testShowHideDialog();
224 void testDialogInput();
225 void testCalcSaveAs();
226 void testControlState();
227 void testMetricField();
228 void testMultiDocuments();
231 CPPUNIT_TEST_SUITE(DesktopLOKTest
);
232 CPPUNIT_TEST(testGetStyles
);
233 CPPUNIT_TEST(testGetFonts
);
234 CPPUNIT_TEST(testCreateView
);
235 CPPUNIT_TEST(testGetFilterTypes
);
236 CPPUNIT_TEST(testGetPartPageRectangles
);
237 CPPUNIT_TEST(testSearchCalc
);
238 CPPUNIT_TEST(testSearchAllNotificationsCalc
);
239 CPPUNIT_TEST(testPaintTile
);
240 CPPUNIT_TEST(testSaveAs
);
241 CPPUNIT_TEST(testSaveAsCalc
);
242 CPPUNIT_TEST(testPasteWriter
);
243 CPPUNIT_TEST(testPasteWriterJPEG
);
244 CPPUNIT_TEST(testUndoWriter
);
245 CPPUNIT_TEST(testRowColumnHeaders
);
246 CPPUNIT_TEST(testHiddenRowHeaders
);
247 CPPUNIT_TEST(testCellCursor
);
248 CPPUNIT_TEST(testCommandResult
);
249 CPPUNIT_TEST(testWriterComments
);
250 CPPUNIT_TEST(testSheetOperations
);
251 CPPUNIT_TEST(testSheetSelections
);
252 CPPUNIT_TEST(testContextMenuCalc
);
253 CPPUNIT_TEST(testContextMenuWriter
);
254 CPPUNIT_TEST(testContextMenuImpress
);
255 CPPUNIT_TEST(testNotificationCompression
);
256 CPPUNIT_TEST(testTileInvalidationCompression
);
257 CPPUNIT_TEST(testPartInInvalidation
);
258 CPPUNIT_TEST(testInput
);
259 CPPUNIT_TEST(testRedlineWriter
);
260 CPPUNIT_TEST(testTrackChanges
);
261 CPPUNIT_TEST(testRedlineCalc
);
262 CPPUNIT_TEST(testPaintPartTile
);
263 CPPUNIT_TEST(testWriterCommentInsertCursor
);
265 CPPUNIT_TEST(testGetFontSubset
);
267 CPPUNIT_TEST(testCommentsWriter
);
268 CPPUNIT_TEST(testCommentsCalc
);
269 CPPUNIT_TEST(testCommentsImpress
);
270 CPPUNIT_TEST(testCommentsCallbacksWriter
);
271 CPPUNIT_TEST(testCommentsAddEditDeleteDraw
);
272 CPPUNIT_TEST(testRunMacro
);
273 CPPUNIT_TEST(testExtractParameter
);
274 CPPUNIT_TEST(testGetSignatureState_Signed
);
275 CPPUNIT_TEST(testGetSignatureState_NonSigned
);
277 CPPUNIT_TEST(testInsertCertificate_DER_ODT
);
278 CPPUNIT_TEST(testInsertCertificate_PEM_ODT
);
279 CPPUNIT_TEST(testInsertCertificate_PEM_DOCX
);
280 CPPUNIT_TEST(testSignDocument_PEM_PDF
);
282 CPPUNIT_TEST(testTextSelectionHandles
);
283 CPPUNIT_TEST(testComplexSelection
);
284 CPPUNIT_TEST(testSpellcheckerMultiView
);
285 CPPUNIT_TEST(testDialogPaste
);
286 CPPUNIT_TEST(testShowHideDialog
);
287 CPPUNIT_TEST(testDialogInput
);
288 CPPUNIT_TEST(testCalcSaveAs
);
289 CPPUNIT_TEST(testControlState
);
290 CPPUNIT_TEST(testMetricField
);
291 CPPUNIT_TEST(testMultiDocuments
);
292 CPPUNIT_TEST(testABI
);
293 CPPUNIT_TEST_SUITE_END();
295 uno::Reference
<lang::XComponent
> mxComponent
;
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 static Control
* GetFocusControl(vcl::Window
const * pParent
)
322 sal_uInt16 nChildren
= pParent
->GetChildCount();
323 for (sal_uInt16 nChild
= 0; nChild
< nChildren
; ++nChild
)
325 vcl::Window
* pChild
= pParent
->GetChild( nChild
);
326 Control
* pCtrl
= dynamic_cast<Control
*>(pChild
);
327 if (pCtrl
&& pCtrl
->HasControlFocus())
330 Control
* pSubCtrl
= GetFocusControl( pChild
);
337 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
338 DesktopLOKTest::loadDocUrlImpl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
343 case LOK_DOCTYPE_TEXT
:
344 aService
= "com.sun.star.text.TextDocument";
346 case LOK_DOCTYPE_SPREADSHEET
:
347 aService
= "com.sun.star.sheet.SpreadsheetDocument";
349 case LOK_DOCTYPE_PRESENTATION
:
350 aService
= "com.sun.star.presentation.PresentationDocument";
353 CPPUNIT_ASSERT(false);
357 static int nDocumentIdCounter
= 0;
358 SfxViewShell::SetCurrentDocId(ViewShellDocId(nDocumentIdCounter
));
359 uno::Reference
<lang::XComponent
> xComponent
= loadFromDesktop(rFileURL
, aService
);
360 CPPUNIT_ASSERT(xComponent
.is());
362 std::unique_ptr
<LibLODocument_Impl
> pDocument(new LibLODocument_Impl(xComponent
, nDocumentIdCounter
));
363 ++nDocumentIdCounter
;
365 return std::make_pair(std::move(pDocument
), xComponent
);
368 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
369 DesktopLOKTest::loadDocImpl(const char* pName
, LibreOfficeKitDocumentType eType
)
372 createFileURL(OUString::createFromAscii(pName
), aFileURL
);
373 return loadDocUrlImpl(aFileURL
, eType
);
376 LibLODocument_Impl
* DesktopLOKTest::loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
378 std::tie(m_pDocument
, mxComponent
) = loadDocUrlImpl(rFileURL
, eType
);
379 return m_pDocument
.get();
382 LibLODocument_Impl
* DesktopLOKTest::loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
)
384 std::tie(m_pDocument
, mxComponent
) = loadDocImpl(pName
, eType
);
385 return m_pDocument
.get();
388 void DesktopLOKTest::closeDoc(std::unique_ptr
<LibLODocument_Impl
>& pDocument
,
389 uno::Reference
<lang::XComponent
>& xComponent
)
393 pDocument
->pClass
->registerCallback(pDocument
.get(), nullptr, nullptr);
399 closeDocument(xComponent
);
404 void DesktopLOKTest::callback(int nType
, const char* pPayload
, void* pData
)
406 static_cast<DesktopLOKTest
*>(pData
)->callbackImpl(nType
, pPayload
);
409 void DesktopLOKTest::callbackImpl(int nType
, const char* pPayload
)
413 case LOK_CALLBACK_TEXT_SELECTION
:
415 m_aTextSelection
= pPayload
;
416 if (m_aSearchResultSelection
.empty())
417 ++m_nSelectionBeforeSearchResult
;
419 ++m_nSelectionAfterSearchResult
;
422 case LOK_CALLBACK_TEXT_SELECTION_START
:
423 m_aTextSelectionStart
= pPayload
;
425 case LOK_CALLBACK_TEXT_SELECTION_END
:
426 m_aTextSelectionEnd
= pPayload
;
428 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
430 m_aSearchResultSelection
.clear();
431 boost::property_tree::ptree aTree
;
432 std::stringstream
aStream(pPayload
);
433 boost::property_tree::read_json(aStream
, aTree
);
434 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("searchResultSelection"))
436 m_aSearchResultSelection
.emplace_back(rValue
.second
.get
<std::string
>("rectangles").c_str());
437 m_aSearchResultPart
.push_back(std::atoi(rValue
.second
.get
<std::string
>("part").c_str()));
441 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
443 m_aCommandResult
= pPayload
;
444 m_aCommandResultCondition
.set();
447 case LOK_CALLBACK_STATE_CHANGED
:
449 OString
aPayload(pPayload
);
450 OString
aPrefix(".uno:ModifiedStatus=");
451 if (aPayload
.startsWith(aPrefix
))
453 m_bModified
= aPayload
.copy(aPrefix
.getLength()).toBoolean();
454 m_aStateChangedCondition
.set();
456 else if (aPayload
.startsWith(".uno:TrackChanges=") && aPayload
.endsWith("=true"))
460 case LOK_CALLBACK_CONTEXT_MENU
:
462 m_aContextMenuResult
.clear();
463 std::stringstream
aStream(pPayload
);
464 boost::property_tree::read_json(aStream
, m_aContextMenuResult
);
465 m_aContextMenuCondition
.set();
471 void DesktopLOKTest::testGetStyles()
473 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
474 boost::property_tree::ptree aTree
;
475 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:StyleApply");
476 std::stringstream
aStream(pJSON
);
477 boost::property_tree::read_json(aStream
, aTree
);
478 CPPUNIT_ASSERT( !aTree
.empty() );
479 CPPUNIT_ASSERT_EQUAL( std::string(".uno:StyleApply"), aTree
.get_child("commandName").get_value
<std::string
>() );
481 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
482 CPPUNIT_ASSERT( !aValues
.empty() );
483 for (const auto& rPair
: aValues
)
485 if( rPair
.first
!= "ClearStyle")
487 CPPUNIT_ASSERT( !rPair
.second
.empty());
489 if (rPair
.first
!= "CharacterStyles" &&
490 rPair
.first
!= "ParagraphStyles" &&
491 rPair
.first
!= "FrameStyles" &&
492 rPair
.first
!= "PageStyles" &&
493 rPair
.first
!= "NumberingStyles" &&
494 rPair
.first
!= "CellStyles" &&
495 rPair
.first
!= "ShapeStyles" &&
496 rPair
.first
!= "TableStyles" &&
497 rPair
.first
!= "HeaderFooter" &&
498 rPair
.first
!= "Commands")
500 CPPUNIT_FAIL("Unknown style family: " + rPair
.first
);
505 void DesktopLOKTest::testGetFonts()
507 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
508 boost::property_tree::ptree aTree
;
509 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CharFontName");
510 std::stringstream
aStream(pJSON
);
511 boost::property_tree::read_json(aStream
, aTree
);
512 CPPUNIT_ASSERT( !aTree
.empty() );
513 CPPUNIT_ASSERT_EQUAL( std::string(".uno:CharFontName"), aTree
.get_child("commandName").get_value
<std::string
>() );
515 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
516 CPPUNIT_ASSERT( !aValues
.empty() );
517 for (const auto& rPair
: aValues
)
519 // check that we have font sizes available for each font
520 CPPUNIT_ASSERT( !rPair
.second
.empty());
525 void DesktopLOKTest::testCreateView()
527 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
528 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
530 int nId0
= pDocument
->m_pDocumentClass
->getView(pDocument
);
531 int nId1
= pDocument
->m_pDocumentClass
->createView(pDocument
);
532 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
534 // Test getViewIds().
535 std::vector
<int> aViewIds(2);
536 CPPUNIT_ASSERT(pDocument
->m_pDocumentClass
->getViewIds(pDocument
, aViewIds
.data(), aViewIds
.size()));
537 CPPUNIT_ASSERT_EQUAL(nId0
, aViewIds
[0]);
538 CPPUNIT_ASSERT_EQUAL(nId1
, aViewIds
[1]);
540 // Make sure the created view is the active one, then switch to the old
542 CPPUNIT_ASSERT_EQUAL(nId1
, pDocument
->m_pDocumentClass
->getView(pDocument
));
543 pDocument
->m_pDocumentClass
->setView(pDocument
, nId0
);
544 CPPUNIT_ASSERT_EQUAL(nId0
, pDocument
->m_pDocumentClass
->getView(pDocument
));
546 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nId1
);
547 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
550 void DesktopLOKTest::testGetPartPageRectangles()
552 // Test that we get as many page rectangles as expected: blank document is
554 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
555 char* pRectangles
= pDocument
->pClass
->getPartPageRectangles(pDocument
);
556 OUString sRectangles
= OUString::fromUtf8(pRectangles
);
558 std::vector
<OUString
> aRectangles
;
559 sal_Int32 nIndex
= 0;
562 OUString aRectangle
= sRectangles
.getToken(0, ';', nIndex
);
563 if (!aRectangle
.isEmpty())
564 aRectangles
.push_back(aRectangle
);
567 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aRectangles
.size());
572 void DesktopLOKTest::testGetFilterTypes()
574 LibLibreOffice_Impl aOffice
;
575 char* pJSON
= aOffice
.m_pOfficeClass
->getFilterTypes(&aOffice
);
577 std::stringstream
aStream(pJSON
);
578 boost::property_tree::ptree aTree
;
579 boost::property_tree::read_json(aStream
, aTree
);
581 CPPUNIT_ASSERT(!aTree
.empty());
582 CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.oasis.opendocument.text"), aTree
.get_child("writer8").get_child("MediaType").get_value
<std::string
>());
586 void DesktopLOKTest::testSearchCalc()
588 LibLibreOffice_Impl aOffice
;
589 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
590 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
591 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
593 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
595 {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
596 {"SearchItem.Backward", uno::makeAny(false)},
597 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
599 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues
);
600 Scheduler::ProcessEventsToIdle();
602 std::vector
<OString
> aSelections
;
603 sal_Int32 nIndex
= 0;
606 OString aToken
= m_aTextSelection
.getToken(0, ';', nIndex
);
607 aSelections
.push_back(aToken
);
608 } while (nIndex
>= 0);
609 // This was 1, find-all only found one match.
610 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aSelections
.size());
611 // Make sure that we get exactly as many rectangle lists as matches.
612 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), m_aSearchResultSelection
.size());
613 // Result is on the first sheet.
614 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart
[0]);
617 void DesktopLOKTest::testSearchAllNotificationsCalc()
619 LibLibreOffice_Impl aOffice
;
620 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
621 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
622 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
624 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
626 {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
627 {"SearchItem.Backward", uno::makeAny(false)},
628 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
630 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues
);
631 Scheduler::ProcessEventsToIdle();
633 // This was 1, make sure that we get no notifications about selection changes during search.
634 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult
);
635 // But we do get the selection afterwards.
636 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult
> 0);
639 void DesktopLOKTest::testPaintTile()
641 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
642 int nCanvasWidth
= 100;
643 int nCanvasHeight
= 300;
644 sal_Int32 nStride
= cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32
, nCanvasWidth
);
645 std::vector
<unsigned char> aBuffer(nStride
* nCanvasHeight
);
648 int nTileWidth
= 1000;
649 int nTileHeight
= 3000;
651 // This used to crash: paintTile() implementation did not handle
652 // nCanvasWidth != nCanvasHeight correctly, as usually both are just always
654 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
656 // This crashed in OutputDevice::DrawDeviceAlphaBitmap().
661 aBuffer
.resize(nCanvasWidth
* nCanvasHeight
* 4);
662 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
665 void DesktopLOKTest::testSaveAs()
667 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
668 utl::TempFile aTempFile
;
669 aTempFile
.EnableKillingFile();
670 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
673 void DesktopLOKTest::testSaveAsCalc()
675 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
676 utl::TempFile aTempFile
;
677 aTempFile
.EnableKillingFile();
678 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
681 void DesktopLOKTest::testPasteWriter()
683 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
684 OString
aText("hello");
686 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
688 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
689 Scheduler::ProcessEventsToIdle();
690 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
691 CPPUNIT_ASSERT_EQUAL(OString("hello"), OString(pText
));
694 // textt/plain should be rejected.
695 CPPUNIT_ASSERT(!pDocument
->pClass
->paste(pDocument
, "textt/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
696 // Writer is expected to support text/html.
697 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aText
.getStr(), aText
.getLength()));
699 // Overwrite doc contents with a HTML paste.
700 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
701 Scheduler::ProcessEventsToIdle();
702 OString
aComment("foo <!-- bar --> baz");
703 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aComment
.getStr(), aComment
.getLength()));
705 // Check if we have a comment.
706 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
707 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
708 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
709 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
710 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
711 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
712 CPPUNIT_ASSERT_EQUAL(OUString("Text"), xTextPortion
->getPropertyValue("TextPortionType").get
<OUString
>());
713 // Without the accompanying fix in place, this test would have failed, as we had a comment
714 // between "foo" and "baz".
715 CPPUNIT_ASSERT(!xTextPortionEnumeration
->hasMoreElements());
718 void DesktopLOKTest::testPasteWriterJPEG()
720 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
723 createFileURL("paste.jpg", aFileURL
);
724 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
725 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
727 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
729 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
730 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
731 // This was 0, JPEG was not handled as a format for clipboard paste.
732 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(1), xDrawPage
->getCount());
734 uno::Reference
<beans::XPropertySet
> xShape(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
735 // This was text::TextContentAnchorType_AT_PARAGRAPH.
736 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER
, xShape
->getPropertyValue("AnchorType").get
<text::TextContentAnchorType
>());
738 // Delete the pasted picture, and paste again with a custom anchor type.
739 uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY_THROW
)->dispose();
740 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
742 {"AnchorType", uno::makeAny(static_cast<sal_uInt16
>(text::TextContentAnchorType_AT_CHARACTER
))},
744 comphelper::dispatchCommand(".uno:Paste", aPropertyValues
);
745 xShape
.set(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
746 // This was text::TextContentAnchorType_AS_CHARACTER, AnchorType argument was ignored.
747 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AT_CHARACTER
, xShape
->getPropertyValue("AnchorType").get
<text::TextContentAnchorType
>());
750 void DesktopLOKTest::testUndoWriter()
752 // Load a Writer document and press a key.
753 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
754 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
755 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
756 Scheduler::ProcessEventsToIdle();
758 boost::property_tree::ptree aTree
;
759 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:Undo");
760 std::stringstream
aStream(pJSON
);
762 CPPUNIT_ASSERT(!aStream
.str().empty());
763 boost::property_tree::read_json(aStream
, aTree
);
764 // Make sure that pressing a key creates exactly one undo action.
765 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("actions").size());
768 void DesktopLOKTest::testRowColumnHeaders()
776 * "size": "254.987250637468",
780 * "size": "509.974501274936",
786 * "size": "1274.93625318734",
790 * "size": "2549.87250637468",
796 * "size" defines the bottom/right boundary of a row/column in twips (size between 0 and boundary)
797 * "text" has the header label in UTF-8
799 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
801 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
805 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
806 long nX
= rtl::math::round(nWidth
/ 4.0);
807 long nY
= rtl::math::round(nHeight
/ 4.0);
808 nWidth
= rtl::math::round(nWidth
/ 2.0);
809 nHeight
= rtl::math::round(nHeight
/ 2.0);
811 std::stringstream aPayload
;
812 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
814 boost::property_tree::ptree aTree
;
815 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
816 std::stringstream
aStream(pJSON
);
819 CPPUNIT_ASSERT(!aStream
.str().empty());
821 boost::property_tree::read_json(aStream
, aTree
);
822 sal_Int32 nPrevious
= 0;
823 bool bFirstHeader
= true;
824 bool bNotEnoughHeaders
= true;
825 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
827 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
828 nSize
*= 15; /* TWIPS_PER_PIXEL */
829 OString
aText(rValue
.second
.get
<std::string
>("text").c_str());
833 CPPUNIT_ASSERT(nSize
<= nY
);
834 CPPUNIT_ASSERT_EQUAL(OString("10"), aText
);
835 bFirstHeader
= false;
839 CPPUNIT_ASSERT(nSize
> 0);
840 CPPUNIT_ASSERT(nPrevious
< nSize
);
841 if (nSize
> nY
+ nHeight
)
843 bNotEnoughHeaders
= false;
849 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
853 bNotEnoughHeaders
= true;
854 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("columns"))
856 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
857 nSize
*= 15; /* TWIPS_PER_PIXEL */
858 OString
aText(rValue
.second
.get
<std::string
>("text").c_str());
861 CPPUNIT_ASSERT(nSize
<= nX
);
862 CPPUNIT_ASSERT_EQUAL(OString("3"), aText
);
863 bFirstHeader
= false;
867 CPPUNIT_ASSERT(nSize
> 0);
868 CPPUNIT_ASSERT(nPrevious
< nSize
);
869 if (nSize
> nX
+ nWidth
)
871 bNotEnoughHeaders
= false;
877 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
880 void DesktopLOKTest::testHiddenRowHeaders()
882 LibLODocument_Impl
* pDocument
= loadDoc("hidden-row.ods");
884 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
890 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
892 std::stringstream aPayload
;
893 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
895 boost::property_tree::ptree aTree
;
896 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
897 std::stringstream
aStream(pJSON
);
899 CPPUNIT_ASSERT(!aStream
.str().empty());
901 boost::property_tree::read_json(aStream
, aTree
);
902 sal_Int32 nPrevious
= 0;
903 sal_Int32 nIndex
= 0;
904 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
906 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
910 // nSize was 510, nPrevious was 255, i.e. hidden row wasn't reported as 0 height.
911 CPPUNIT_ASSERT_EQUAL(nPrevious
, nSize
);
918 void DesktopLOKTest::testCellCursor()
920 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
922 boost::property_tree::ptree aTree
;
924 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CellCursor?tileWidth=1&tileHeight=1&outputWidth=1&outputHeight=1");
926 std::stringstream
aStream(pJSON
);
928 CPPUNIT_ASSERT(!aStream
.str().empty());
930 boost::property_tree::read_json(aStream
, aTree
);
932 OString
aRectangle(aTree
.get
<std::string
>("commandValues").c_str());
933 // cell cursor geometry + col + row
934 CPPUNIT_ASSERT_EQUAL(OString("0, 0, 1274, 254, 0, 0"), aRectangle
);
937 void DesktopLOKTest::testCommandResult()
939 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
941 // the postUnoCommand() is supposed to be async, let's test it safely
942 // [no idea if it is async in reality - most probably we are operating
943 // under some solar mutex or something anyway ;-) - but...]
944 TimeValue aTimeValue
= { 2 , 0 }; // 2 seconds max
946 // nothing is triggered when we have no callback yet, we just time out on
947 // the condition var.
948 m_aCommandResultCondition
.reset();
949 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
950 Scheduler::ProcessEventsToIdle();
951 m_aCommandResultCondition
.wait(aTimeValue
);
953 CPPUNIT_ASSERT(m_aCommandResult
.isEmpty());
955 // but we get some real values when the callback is set up
956 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
958 m_aCommandResultCondition
.reset();
959 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
960 Scheduler::ProcessEventsToIdle();
961 m_aCommandResultCondition
.wait(aTimeValue
);
963 boost::property_tree::ptree aTree
;
964 std::stringstream
aStream(m_aCommandResult
.getStr());
965 boost::property_tree::read_json(aStream
, aTree
);
967 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), aTree
.get_child("commandName").get_value
<std::string
>());
968 CPPUNIT_ASSERT_EQUAL(true, aTree
.get_child("success").get_value
<bool>());
971 void DesktopLOKTest::testWriterComments()
973 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
974 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
975 uno::Reference
<awt::XReschedule
> xToolkit
= com::sun::star::awt::Toolkit::create(comphelper::getProcessComponentContext());
977 // Insert a comment at the beginning of the document and wait till the main
978 // loop grabs the focus, so characters end up in the annotation window.
979 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
980 m_aCommandResultCondition
.reset();
981 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", nullptr, true);
982 Scheduler::ProcessEventsToIdle();
983 m_aCommandResultCondition
.wait(aTimeValue
);
984 CPPUNIT_ASSERT(!m_aCommandResult
.isEmpty());
985 xToolkit
->reschedule();
987 // Test that we have a comment.
988 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
989 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
990 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
991 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
992 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
993 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
994 CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), xTextPortion
->getPropertyValue("TextPortionType").get
<OUString
>());
996 // Type "test" and finish editing.
997 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
998 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'e', 0);
999 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 's', 0);
1000 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1001 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
1002 Scheduler::ProcessEventsToIdle();
1004 // Test that the typed characters ended up in the right window.
1005 auto xTextField
= xTextPortion
->getPropertyValue("TextField").get
< uno::Reference
<beans::XPropertySet
> >();
1006 // This was empty, typed characters ended up in the body text.
1007 CPPUNIT_ASSERT_EQUAL(OUString("test"), xTextField
->getPropertyValue("Content").get
<OUString
>());
1010 void DesktopLOKTest::testTrackChanges()
1012 // Load a document and create two views.
1013 LibLibreOffice_Impl aOffice
;
1014 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1015 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1016 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1017 pDocument
->pClass
->createView(pDocument
);
1018 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1019 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1020 Scheduler::ProcessEventsToIdle();
1022 // Enable trak changes and assert that both views get notified.
1023 m_nTrackChanges
= 0;
1024 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:TrackChanges", nullptr, false);
1025 Scheduler::ProcessEventsToIdle();
1026 // This was 1, only the active view was notified.
1027 CPPUNIT_ASSERT_EQUAL(2, m_nTrackChanges
);
1030 void DesktopLOKTest::testSheetOperations()
1032 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
1034 // insert the last sheet
1035 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
1036 "{ \"Name\": { \"type\": \"string\", \"value\": \"LastSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 0 } }", false);
1038 // insert the first sheet
1039 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
1040 "{ \"Name\": { \"type\": \"string\", \"value\": \"FirstSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 1 } }", false);
1042 // rename the \"Sheet1\" (2nd now) to \"Renamed\"
1043 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Name",
1044 "{ \"Name\": { \"type\": \"string\", \"value\": \"Renamed\" }, \"Index\": { \"type\": \"long\", \"value\": 2 } }", false);
1046 // delete the \"Sheet2\" (3rd)
1047 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Remove",
1048 "{ \"Index\": { \"type\": \"long\", \"value\": 3 } }", false);
1050 Scheduler::ProcessEventsToIdle();
1051 CPPUNIT_ASSERT_EQUAL(6, pDocument
->pClass
->getParts(pDocument
));
1053 std::vector
<OString
> aExpected
= { "FirstSheet", "Renamed", "Sheet3", "Sheet4", "Sheet5", "LastSheet" };
1054 for (int i
= 0; i
< 6; ++i
)
1056 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], OString(pDocument
->pClass
->getPartName(pDocument
, i
)));
1060 void DesktopLOKTest::testSheetSelections()
1062 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods", LOK_DOCTYPE_SPREADSHEET
);
1063 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1064 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1067 * Check if selection data is correct
1072 int const col2
= 2200;
1073 int const col3
= 3300;
1077 // Select row 5 from column 1 through column 5
1078 pDocument
->pClass
->postMouseEvent(pDocument
,
1079 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1082 pDocument
->pClass
->postMouseEvent(pDocument
,
1083 LOK_MOUSEEVENT_MOUSEMOVE
,
1086 pDocument
->pClass
->postMouseEvent(pDocument
,
1087 LOK_MOUSEEVENT_MOUSEMOVE
,
1090 pDocument
->pClass
->postMouseEvent(pDocument
,
1091 LOK_MOUSEEVENT_MOUSEMOVE
,
1094 pDocument
->pClass
->postMouseEvent(pDocument
,
1095 LOK_MOUSEEVENT_MOUSEMOVE
,
1098 pDocument
->pClass
->postMouseEvent(pDocument
,
1099 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1102 Scheduler::ProcessEventsToIdle();
1104 // Copy the contents and check if matches expected data
1106 char* pUsedMimeType
= nullptr;
1107 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1108 std::vector
<long> aExpected
= {5, 6, 7, 8, 9};
1109 std::istringstream
iss(pCopiedContent
);
1110 for (size_t i
= 0; i
< aExpected
.size(); i
++)
1114 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], strtol(token
.c_str(), nullptr, 10));
1117 free(pUsedMimeType
);
1118 free(pCopiedContent
);
1122 * Check if clicking inside the selection deselects the whole selection
1124 int const row10
= 2400;
1125 // Select starting from row5, col1 to row10, col5
1126 pDocument
->pClass
->postMouseEvent(pDocument
,
1127 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1130 pDocument
->pClass
->postMouseEvent(pDocument
,
1131 LOK_MOUSEEVENT_MOUSEMOVE
,
1134 pDocument
->pClass
->postMouseEvent(pDocument
,
1135 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1139 // Click at row5, col4
1140 pDocument
->pClass
->postMouseEvent(pDocument
,
1141 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1144 pDocument
->pClass
->postMouseEvent(pDocument
,
1145 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1148 Scheduler::ProcessEventsToIdle();
1150 // Selected text should get deselected and copying should give us
1151 // content of only one cell, now
1153 char* pUsedMimeType
= nullptr;
1154 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1155 std::vector
<long> aExpected
= { 8 };
1156 std::istringstream
iss(pCopiedContent
);
1157 for (size_t i
= 0; i
< aExpected
.size(); i
++)
1161 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], strtol(token
.c_str(), nullptr, 10));
1164 free(pUsedMimeType
);
1165 free(pCopiedContent
);
1171 void verifyContextMenuStructure(boost::property_tree::ptree
& aRoot
)
1173 for (const auto& aItemPair
: aRoot
)
1175 // This is an array, so no key
1176 CPPUNIT_ASSERT_EQUAL(aItemPair
.first
, std::string(""));
1178 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1179 boost::optional
<boost::property_tree::ptree
&> aText
= aItemValue
.get_child_optional("text");
1180 boost::optional
<boost::property_tree::ptree
&> aType
= aItemValue
.get_child_optional("type");
1181 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1182 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aItemValue
.get_child_optional("menu");
1183 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aItemValue
.get_child_optional("enabled");
1184 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aItemValue
.get_child_optional("checktype");
1185 boost::optional
<boost::property_tree::ptree
&> aChecked
= aItemValue
.get_child_optional("checked");
1187 // type is omnipresent
1188 CPPUNIT_ASSERT( aType
);
1190 // separator doesn't have any other attribs
1191 if ( aType
.get().data() == "separator" )
1193 CPPUNIT_ASSERT( !aText
&& !aCommand
&& !aSubmenu
&& !aEnabled
&& !aChecktype
&& !aChecked
);
1195 else if ( aType
.get().data() == "command" )
1197 CPPUNIT_ASSERT( aCommand
&& aText
);
1199 else if ( aType
.get().data() == "menu")
1201 CPPUNIT_ASSERT( aSubmenu
&& aText
);
1202 verifyContextMenuStructure( aSubmenu
.get() );
1207 CPPUNIT_ASSERT( aChecktype
.get().data() == "radio" ||
1208 aChecktype
.get().data() == "checkmark" ||
1209 aChecktype
.get().data() == "auto" );
1211 CPPUNIT_ASSERT( aChecked
&&
1212 ( aChecked
.get().data() == "true" || aChecked
.get().data() == "false" ) );
1218 boost::optional
<boost::property_tree::ptree
>
1219 getContextMenuItem(boost::property_tree::ptree
& aMenu
, std::string
const & unoSelector
)
1221 boost::optional
<boost::property_tree::ptree
> aMenuItem
;
1222 for (const auto& aItemPair
: aMenu
)
1224 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1226 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1227 if (aCommand
&& aCommand
.get().data() == unoSelector
)
1229 aMenuItem
= aItemValue
;
1237 } // end anonymous namespace
1239 void DesktopLOKTest::testContextMenuCalc()
1241 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
1242 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1243 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1246 Point
aPointOnImage(1150, 1100);
1247 pDocument
->pClass
->postMouseEvent(pDocument
,
1248 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1249 aPointOnImage
.X(), aPointOnImage
.Y(),
1251 Scheduler::ProcessEventsToIdle();
1253 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1254 m_aContextMenuCondition
.wait(aTimeValue
);
1256 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1257 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1258 CPPUNIT_ASSERT( aMenu
);
1259 verifyContextMenuStructure( aMenu
.get() );
1261 // tests for calc specific context menu
1264 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1265 CPPUNIT_ASSERT(aMenuItem
);
1267 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1268 CPPUNIT_ASSERT(aEnabled
);
1269 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1274 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1275 CPPUNIT_ASSERT(aMenuItem
);
1277 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1278 CPPUNIT_ASSERT(aEnabled
);
1279 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1284 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1285 CPPUNIT_ASSERT(aMenuItem
);
1287 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1288 CPPUNIT_ASSERT(aEnabled
);
1289 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1292 // Remove hyperlink is disabled
1294 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:DeleteShapeHyperlink");
1295 CPPUNIT_ASSERT(aMenuItem
);
1297 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1298 CPPUNIT_ASSERT(aEnabled
);
1299 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1302 // open hyperlink is disabled
1304 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:OpenHyperlinkOnCursor");
1305 CPPUNIT_ASSERT(aMenuItem
);
1307 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1308 CPPUNIT_ASSERT(aEnabled
);
1309 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1312 // checkbutton tests
1314 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:AnchorMenu");
1315 CPPUNIT_ASSERT(aMenuItem
);
1317 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1318 CPPUNIT_ASSERT(aSubmenu
);
1320 boost::optional
<boost::property_tree::ptree
> aMenuItemToPage
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToPage");
1321 CPPUNIT_ASSERT(aMenuItemToPage
);
1323 boost::optional
<boost::property_tree::ptree
> aMenuItemToCell
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToCell");
1324 CPPUNIT_ASSERT(aMenuItemToCell
);
1326 // these are radio buttons
1327 boost::optional
<boost::property_tree::ptree
&> aChecktypeToPage
= aMenuItemToPage
.get().get_child_optional("checktype");
1328 CPPUNIT_ASSERT(aChecktypeToPage
);
1329 CPPUNIT_ASSERT_EQUAL(aChecktypeToPage
.get().data(), std::string("radio"));
1331 boost::optional
<boost::property_tree::ptree
&> aChecktypeToCell
= aMenuItemToCell
.get().get_child_optional("checktype");
1332 CPPUNIT_ASSERT(aChecktypeToCell
);
1333 CPPUNIT_ASSERT_EQUAL(aChecktypeToCell
.get().data(), std::string("radio"));
1335 // ToPage is checked
1336 boost::optional
<boost::property_tree::ptree
&> aCheckedToPage
= aMenuItemToPage
.get().get_child_optional("checked");
1337 CPPUNIT_ASSERT(aCheckedToPage
);
1338 CPPUNIT_ASSERT_EQUAL(aCheckedToPage
.get().data(), std::string("true"));
1340 // ToCell is unchecked
1341 boost::optional
<boost::property_tree::ptree
&> aCheckedToCell
= aMenuItemToCell
.get().get_child_optional("checked");
1342 CPPUNIT_ASSERT(aCheckedToCell
);
1343 CPPUNIT_ASSERT_EQUAL(aCheckedToCell
.get().data(), std::string("false"));
1347 void DesktopLOKTest::testContextMenuWriter()
1349 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1350 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1351 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1353 Point
aRandomPoint(1150, 1100);
1354 pDocument
->pClass
->postMouseEvent(pDocument
,
1355 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1356 aRandomPoint
.X(), aRandomPoint
.Y(),
1358 Scheduler::ProcessEventsToIdle();
1360 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1361 m_aContextMenuCondition
.wait(aTimeValue
);
1363 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1364 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1365 CPPUNIT_ASSERT( aMenu
);
1366 verifyContextMenuStructure( aMenu
.get() );
1368 // tests for writer specific context menu
1371 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1372 CPPUNIT_ASSERT(aMenuItem
);
1374 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1375 CPPUNIT_ASSERT(aEnabled
);
1376 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1381 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1382 CPPUNIT_ASSERT(aMenuItem
);
1384 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1385 CPPUNIT_ASSERT(aEnabled
);
1386 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1391 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1392 CPPUNIT_ASSERT(aMenuItem
);
1394 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1395 CPPUNIT_ASSERT(aEnabled
);
1396 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1400 void DesktopLOKTest::testContextMenuImpress()
1402 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp", LOK_DOCTYPE_PRESENTATION
);
1403 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1404 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1406 // random point where we don't hit an underlying comment or text box
1407 Point
aRandomPoint(10, 1150);
1408 pDocument
->pClass
->postMouseEvent(pDocument
,
1409 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1410 aRandomPoint
.X(), aRandomPoint
.Y(),
1412 Scheduler::ProcessEventsToIdle();
1414 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1415 m_aContextMenuCondition
.wait(aTimeValue
);
1417 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1418 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1419 CPPUNIT_ASSERT( aMenu
);
1420 verifyContextMenuStructure( aMenu
.get() );
1422 // tests for impress specific context menu
1425 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1426 CPPUNIT_ASSERT(aMenuItem
);
1428 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1429 CPPUNIT_ASSERT(aEnabled
);
1430 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1435 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1436 CPPUNIT_ASSERT(aMenuItem
);
1438 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1439 CPPUNIT_ASSERT(aEnabled
);
1440 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1445 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1446 CPPUNIT_ASSERT(aMenuItem
);
1448 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1449 CPPUNIT_ASSERT(aEnabled
);
1450 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1453 // SaveBackground is disabled
1455 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SaveBackground");
1456 CPPUNIT_ASSERT(aMenuItem
);
1458 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1459 CPPUNIT_ASSERT(aEnabled
);
1460 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1463 // checkbutton tests
1465 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:ShowRuler");
1466 CPPUNIT_ASSERT(aMenuItem
);
1468 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aMenuItem
.get().get_child_optional("checktype");
1469 CPPUNIT_ASSERT(aChecktype
);
1470 CPPUNIT_ASSERT_EQUAL(aChecktype
.get().data(), std::string("checkmark"));
1472 boost::optional
<boost::property_tree::ptree
&> aChecked
= aMenuItem
.get().get_child_optional("checked");
1473 CPPUNIT_ASSERT(aChecked
);
1474 CPPUNIT_ASSERT_EQUAL(aChecked
.get().data(), std::string("false"));
1477 // Checkbutton tests inside SnapLines submenu
1479 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SnapLinesMenu");
1480 CPPUNIT_ASSERT(aMenuItem
);
1482 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1483 CPPUNIT_ASSERT(aSubmenu
);
1485 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpVis
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesVisible");
1486 CPPUNIT_ASSERT(aMenuItemHelpVis
);
1488 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpUse
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesUse");
1489 CPPUNIT_ASSERT(aMenuItemHelpUse
);
1491 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpFront
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesFront");
1492 CPPUNIT_ASSERT(aMenuItemHelpFront
);
1494 // these are checkmarks
1495 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checktype");
1496 CPPUNIT_ASSERT(aChecktypeHelpVis
);
1497 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpVis
.get().data(), std::string("checkmark"));
1499 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checktype");
1500 CPPUNIT_ASSERT(aChecktypeHelpUse
);
1501 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpUse
.get().data(), std::string("checkmark"));
1503 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checktype");
1504 CPPUNIT_ASSERT(aChecktypeHelpFront
);
1505 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpFront
.get().data(), std::string("checkmark"));
1507 // HelplineVisible is unchecked
1508 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checked");
1509 CPPUNIT_ASSERT(aCheckedHelpVis
);
1510 CPPUNIT_ASSERT_EQUAL(aCheckedHelpVis
.get().data(), std::string("false"));
1512 // HelplineUse is checked
1513 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checked");
1514 CPPUNIT_ASSERT(aCheckedHelpUse
);
1515 CPPUNIT_ASSERT_EQUAL(aCheckedHelpUse
.get().data(), std::string("true"));
1517 // HelplineFront is checked
1518 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checked");
1519 CPPUNIT_ASSERT(aCheckedHelpFront
);
1520 CPPUNIT_ASSERT_EQUAL(aCheckedHelpFront
.get().data(), std::string("true"));
1524 static void callbackCompressionTest(const int type
, const char* payload
, void* data
)
1526 std::vector
<std::tuple
<int, std::string
>>* notifs
= static_cast<std::vector
<std::tuple
<int, std::string
>>*>(data
);
1527 notifs
->emplace_back(type
, std::string(payload
? payload
: "(nil)"));
1530 void DesktopLOKTest::testNotificationCompression()
1532 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1533 std::vector
<std::tuple
<int, std::string
>> notifs
;
1534 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1536 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""); // 0
1537 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Superseded.
1538 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""); // Should be dropped.
1539 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // 1
1540 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Should be dropped.
1541 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""); // Superseded.
1542 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""); // 2
1543 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:Bold"); // 3
1544 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""); // 4
1545 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"); // 5
1546 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // Should be dropped.
1547 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // Should be dropped.
1548 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"); // Should be dropped.
1549 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // Superseded.
1550 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // Superseded.
1551 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Superseded.
1552 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // Should be dropped.
1553 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // Should be dropped.
1554 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""); // 7
1555 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // 8
1556 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // 9
1557 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"); // 10
1558 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""); // 11
1559 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"); // Should be dropped.
1560 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"); // 12
1561 handler
->queue(LOK_CALLBACK_SET_PART
, "1"); // 13
1562 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=20"); // Superseded
1563 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""); // Should be dropped.
1564 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"); // Should be dropped.
1565 handler
->queue(LOK_CALLBACK_SET_PART
, "1"); // Should be dropped.
1566 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=1"); // 14
1568 Scheduler::ProcessEventsToIdle();
1570 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(14), notifs
.size());
1573 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
), std::get
<0>(notifs
[i
]));
1574 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1576 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1577 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1579 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1580 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1582 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1583 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), std::get
<1>(notifs
[i
++]));
1585 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1586 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1588 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_MOUSE_POINTER
), std::get
<0>(notifs
[i
]));
1589 CPPUNIT_ASSERT_EQUAL(std::string("text"), std::get
<1>(notifs
[i
++]));
1591 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION
), std::get
<0>(notifs
[i
]));
1592 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1594 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_START
), std::get
<0>(notifs
[i
]));
1595 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1597 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_END
), std::get
<0>(notifs
[i
]));
1598 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1600 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_CURSOR
), std::get
<0>(notifs
[i
]));
1601 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1603 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CURSOR_VISIBLE
), std::get
<0>(notifs
[i
]));
1604 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1606 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_FORMULA
), std::get
<0>(notifs
[i
]));
1607 CPPUNIT_ASSERT_EQUAL(std::string("blah"), std::get
<1>(notifs
[i
++]));
1609 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_SET_PART
), std::get
<0>(notifs
[i
]));
1610 CPPUNIT_ASSERT_EQUAL(std::string("1"), std::get
<1>(notifs
[i
++]));
1612 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1613 CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get
<1>(notifs
[i
++]));
1616 void DesktopLOKTest::testTileInvalidationCompression()
1618 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1620 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1621 comphelper::ScopeGuard
aGuard([]()
1623 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1626 // Single part merging
1628 std::vector
<std::tuple
<int, std::string
>> notifs
;
1629 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1631 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1632 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1633 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -50, 500, 650, 0");
1634 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0");
1635 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "100, 100, 200, 200, 0");
1637 Scheduler::ProcessEventsToIdle();
1639 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1642 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1643 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 400, 600, 0"), std::get
<1>(notifs
[i
++]));
1648 std::vector
<std::tuple
<int, std::string
>> notifs
;
1649 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1651 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1652 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1"); // Different part
1653 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, 2"); // Invalid
1654 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, 0"); // Inside first
1655 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 1"); // Invalid
1657 Scheduler::ProcessEventsToIdle();
1659 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1662 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1663 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 1"), std::get
<1>(notifs
[i
++]));
1665 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1666 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 239, 239, 0"), std::get
<1>(notifs
[i
++]));
1671 std::vector
<std::tuple
<int, std::string
>> notifs
;
1672 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1674 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0"); // 0
1675 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1"); // 1: Different part
1676 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1"); // Invalid
1677 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, -1"); // 0: All parts
1678 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1"); // Invalid
1679 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -100, 1200, 1200, -1"); // 0: All parts
1680 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 3"); // Overlapped
1681 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 2"); // 1: Unique region
1683 Scheduler::ProcessEventsToIdle();
1685 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1688 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1689 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1100, 1100, -1"), std::get
<1>(notifs
[i
++]));
1691 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1692 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 2"), std::get
<1>(notifs
[i
++]));
1695 // All Parts (partial)
1697 std::vector
<std::tuple
<int, std::string
>> notifs
;
1698 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1700 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 0"); // 0
1701 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 100, 100, 1"); // 1: Different part
1702 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1"); // Invalid
1703 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 50, 50, -1"); // 2: All-parts
1704 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1"); // Invalid
1705 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 40, 40, 3"); // Overlapped w/ 2
1706 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 4"); // 3: Unique
1707 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 1"); // 4: Unique
1709 Scheduler::ProcessEventsToIdle();
1711 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), notifs
.size());
1714 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1715 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 0"), std::get
<1>(notifs
[i
++]));
1717 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1718 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 100, 100, 1"), std::get
<1>(notifs
[i
++]));
1720 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1721 CPPUNIT_ASSERT_EQUAL(std::string("150, 150, 50, 50, -1"), std::get
<1>(notifs
[i
++]));
1723 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1724 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 4"), 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("1000, 1000, 1239, 1239, 1"), std::get
<1>(notifs
[i
++]));
1730 // Merge with "EMPTY"
1732 std::vector
<std::tuple
<int, std::string
>> notifs
;
1733 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1735 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1736 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "EMPTY, 0");
1737 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 240, 0");
1738 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 300, 300, 0");
1739 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0");
1741 Scheduler::ProcessEventsToIdle();
1743 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1746 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1747 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1000000000, 1000000000, 0"), std::get
<1>(notifs
[i
++]));
1751 void DesktopLOKTest::testPartInInvalidation()
1753 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1754 // No part in invalidation: merge.
1756 std::vector
<std::tuple
<int, std::string
>> notifs
;
1757 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1759 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10");
1760 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10");
1762 Scheduler::ProcessEventsToIdle();
1764 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1766 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
1767 CPPUNIT_ASSERT_EQUAL(std::string("10, 10, 30, 10"), std::get
<1>(notifs
[0]));
1769 // No part in invalidation: don't merge.
1771 std::vector
<std::tuple
<int, std::string
>> notifs
;
1772 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1774 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10");
1775 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "40, 10, 20, 10");
1777 Scheduler::ProcessEventsToIdle();
1779 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1782 // Part in invalidation, intersection and parts match -> merge.
1784 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1785 comphelper::ScopeGuard
aGuard([]()
1787 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1790 std::vector
<std::tuple
<int, std::string
>> notifs
;
1791 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1793 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0");
1794 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 0");
1796 Scheduler::ProcessEventsToIdle();
1798 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1800 // Part in invalidation, intersection and parts don't match -> don't merge.
1802 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1803 comphelper::ScopeGuard
aGuard([]()
1805 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1808 std::vector
<std::tuple
<int, std::string
>> notifs
;
1809 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1811 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0");
1812 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 1");
1814 Scheduler::ProcessEventsToIdle();
1816 // This failed as RectangleAndPart::Create() always assumed no part in
1817 // payload, so this was merged -> it was 1.
1818 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1822 void DesktopLOKTest::testDialogInput()
1824 comphelper::LibreOfficeKit::setActive();
1825 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1826 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
1827 Scheduler::ProcessEventsToIdle();
1829 SfxViewShell
* pViewShell
= SfxViewShell::Current();
1830 pViewShell
->GetViewFrame()->GetBindings().Update();
1832 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
1833 CPPUNIT_ASSERT(pWindow
);
1835 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
1836 CPPUNIT_ASSERT(pCtrlFocused
);
1837 ComboBox
* pCtrlURL
= dynamic_cast<ComboBox
*>(pCtrlFocused
);
1838 CPPUNIT_ASSERT(pCtrlURL
);
1839 CPPUNIT_ASSERT_EQUAL(OUString(""), pCtrlURL
->GetText());
1841 vcl::LOKWindowId nDialogId
= pWindow
->GetLOKWindowId();
1842 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, nDialogId
, LOK_EXT_TEXTINPUT
, "wiki.");
1843 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, nDialogId
, LOK_EXT_TEXTINPUT_END
, "wiki.");
1844 pDocument
->pClass
->removeTextContext(pDocument
, nDialogId
, 1, 0);
1845 Scheduler::ProcessEventsToIdle();
1846 CPPUNIT_ASSERT_EQUAL(OUString("wiki"), pCtrlURL
->GetText());
1848 static_cast<SystemWindow
*>(pWindow
.get())->Close();
1849 Scheduler::ProcessEventsToIdle();
1852 void DesktopLOKTest::testInput()
1854 // Load a Writer document, enable change recording and press a key.
1855 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1856 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1858 Scheduler::ProcessEventsToIdle(); // Get focus & other bits setup.
1860 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "far");
1861 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "far");
1862 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
1863 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
1864 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "beyond");
1865 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "beyond");
1866 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
1867 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
1869 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "kovely");
1870 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "kovely");
1872 pDocument
->pClass
->removeTextContext(pDocument
, 0, 6, 0);
1873 // Replace it with lovely
1874 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, "lovely");
1875 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, "lovely");
1876 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT
, " ");
1877 pDocument
->pClass
->postWindowExtTextInputEvent(pDocument
, 0, LOK_EXT_TEXTINPUT_END
, " ");
1880 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
1881 Scheduler::ProcessEventsToIdle();
1882 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
1883 CPPUNIT_ASSERT(pText
!= nullptr);
1884 OString
aLovely("far beyond lovely ");
1885 CPPUNIT_ASSERT_EQUAL(aLovely
, OString(pText
));
1889 void DesktopLOKTest::testRedlineWriter()
1891 // Load a Writer document, enable change recording and press a key.
1892 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1893 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1894 xPropertySet
->setPropertyValue("RecordChanges", uno::makeAny(true));
1895 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1896 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
1897 Scheduler::ProcessEventsToIdle();
1899 // Get redline info.
1900 boost::property_tree::ptree aTree
;
1901 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
1902 std::stringstream
aStream(pJSON
);
1904 CPPUNIT_ASSERT(!aStream
.str().empty());
1905 boost::property_tree::read_json(aStream
, aTree
);
1906 // Make sure that pressing a key creates exactly one redline.
1907 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
1909 for (const boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
1910 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
1911 CPPUNIT_ASSERT_EQUAL(std::string("Insert \xE2\x80\x9Ct\xE2\x80\x9D"), rRedline
.second
.get
<std::string
>("description"));
1912 // U+201C LEFT DOUBLE QUOTATION MARK, U+201D RIGHT DOUBLE QUOTATION
1916 void DesktopLOKTest::testRedlineCalc()
1918 // Load a Writer document, enable change recording and press a key.
1919 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
1920 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1921 xPropertySet
->setPropertyValue("RecordChanges", uno::makeAny(true));
1922 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1923 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
1924 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RETURN
);
1925 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RETURN
);
1926 Scheduler::ProcessEventsToIdle();
1928 // Get redline info.
1929 boost::property_tree::ptree aTree
;
1930 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
1931 std::stringstream
aStream(pJSON
);
1933 CPPUNIT_ASSERT(!aStream
.str().empty());
1934 boost::property_tree::read_json(aStream
, aTree
);
1935 // Make sure that pressing a key creates exactly one redline.
1936 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
1938 for (const boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
1939 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
1940 CPPUNIT_ASSERT_EQUAL(std::string("Cell B4 changed from '5' to 't'"), rRedline
.second
.get
<std::string
>("description"));
1945 LibLODocument_Impl
* mpDocument
;
1948 OString m_aCellFormula
;
1949 bool m_bTilesInvalidated
;
1950 tools::Rectangle m_aOwnCursor
;
1951 boost::property_tree::ptree m_aCommentCallbackResult
;
1952 boost::property_tree::ptree m_aCallbackWindowResult
;
1954 ViewCallback(LibLODocument_Impl
* pDocument
)
1955 : mpDocument(pDocument
),
1956 m_bTilesInvalidated(false)
1958 mnView
= SfxLokHelper::getView();
1959 mpDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, this);
1964 mpDocument
->m_pDocumentClass
->setView(mpDocument
, mnView
);
1965 mpDocument
->m_pDocumentClass
->registerCallback(mpDocument
, nullptr, nullptr);
1968 static void callback(int nType
, const char* pPayload
, void* pData
)
1970 static_cast<ViewCallback
*>(pData
)->callbackImpl(nType
, pPayload
);
1973 void callbackImpl(int nType
, const char* pPayload
)
1975 OString
aPayload(pPayload
);
1978 case LOK_CALLBACK_INVALIDATE_TILES
:
1980 m_bTilesInvalidated
= true;
1983 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1985 uno::Sequence
<OUString
> aSeq
= comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload
));
1986 if (OString("EMPTY") == pPayload
)
1988 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(4), aSeq
.getLength());
1989 m_aOwnCursor
.setX(aSeq
[0].toInt32());
1990 m_aOwnCursor
.setY(aSeq
[1].toInt32());
1991 m_aOwnCursor
.setWidth(aSeq
[2].toInt32());
1992 m_aOwnCursor
.setHeight(aSeq
[3].toInt32());
1995 case LOK_CALLBACK_COMMENT
:
1997 m_aCommentCallbackResult
.clear();
1998 std::stringstream
aStream(pPayload
);
1999 boost::property_tree::read_json(aStream
, m_aCommentCallbackResult
);
2000 m_aCommentCallbackResult
= m_aCommentCallbackResult
.get_child("comment");
2003 case LOK_CALLBACK_WINDOW
:
2005 m_aCallbackWindowResult
.clear();
2006 std::stringstream
aStream(pPayload
);
2007 boost::property_tree::read_json(aStream
, m_aCallbackWindowResult
);
2010 case LOK_CALLBACK_CELL_FORMULA
:
2012 m_aCellFormula
= aPayload
;
2019 void DesktopLOKTest::testPaintPartTile()
2021 // Load an impress doc of 2 slides.
2022 // ViewCallback aView1;
2023 // ViewCallback aView2;
2024 LibLODocument_Impl
* pDocument
= loadDoc("2slides.odp");
2025 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2026 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView1);
2027 int nView1
= pDocument
->m_pDocumentClass
->getView(pDocument
);
2029 // Create a second view.
2030 pDocument
->m_pDocumentClass
->createView(pDocument
);
2031 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2032 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView2);
2034 // Go to the second slide in the second view.
2035 pDocument
->m_pDocumentClass
->setPart(pDocument
, 1);
2037 // Switch back to the first view and start typing.
2038 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
2039 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, awt::Key::TAB
);
2040 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, awt::Key::TAB
);
2041 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2042 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2043 Scheduler::ProcessEventsToIdle();
2045 // Call paintPartTile() to paint the second part (in whichever view it finds suitable for this).
2046 unsigned char pPixels
[256 * 256 * 4];
2047 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, pPixels
, 1, 256, 256, 0, 0, 256, 256);
2050 Scheduler::ProcessEventsToIdle();
2051 // aView1.m_bTilesInvalidated = false;
2052 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2053 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2054 Scheduler::ProcessEventsToIdle();
2055 // This failed: paintPartTile() (as a side-effect) ended the text edit of
2056 // the first view, so there were no invalidations.
2057 //CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
2060 void DesktopLOKTest::testWriterCommentInsertCursor()
2062 // Load a document and type a character into the body text of the second view.
2063 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2064 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2065 ViewCallback
aView1(pDocument
);
2066 pDocument
->m_pDocumentClass
->createView(pDocument
);
2067 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2068 ViewCallback
aView2(pDocument
);
2069 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2070 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2071 Scheduler::ProcessEventsToIdle();
2072 tools::Rectangle aBodyCursor
= aView2
.m_aOwnCursor
;
2074 // Now insert a comment and make sure that the comment's cursor is shown,
2075 // not the body text's one.
2076 aView1
.m_aOwnCursor
.SetEmpty();
2077 const int nCtrlAltC
= KEY_MOD1
+ KEY_MOD2
+ 512 + 'c' - 'a';
2078 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'c', nCtrlAltC
);
2079 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'c', nCtrlAltC
);
2080 Scheduler::ProcessEventsToIdle();
2081 // Wait for SfxBindings to actually update the state, which updated the
2083 osl::Thread::wait(std::chrono::seconds(1));
2084 Scheduler::ProcessEventsToIdle();
2085 // This failed: the body cursor was shown right after inserting a comment.
2086 CPPUNIT_ASSERT(aView2
.m_aOwnCursor
.getX() > aBodyCursor
.getX());
2087 // This failed, the first view's cursor also jumped when the second view
2088 // inserted the comment.
2089 CPPUNIT_ASSERT(aView1
.m_aOwnCursor
.IsEmpty());
2091 Scheduler::ProcessEventsToIdle();
2095 void DesktopLOKTest::testGetFontSubset()
2097 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2098 OUString aFontName
= rtl::Uri::encode(
2099 OUString("Liberation Sans"),
2100 rtl_UriCharClassRelSegment
,
2101 rtl_UriEncodeKeepEscapes
,
2102 RTL_TEXTENCODING_UTF8
2104 OString aCommand
= OUStringToOString(".uno:FontSubset&name=" + aFontName
, RTL_TEXTENCODING_UTF8
);
2105 boost::property_tree::ptree aTree
;
2106 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aCommand
.getStr());
2107 std::stringstream
aStream(pJSON
);
2108 boost::property_tree::read_json(aStream
, aTree
);
2109 CPPUNIT_ASSERT( !aTree
.empty() );
2110 CPPUNIT_ASSERT_EQUAL( std::string(".uno:FontSubset"), aTree
.get_child("commandName").get_value
<std::string
>() );
2111 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
2112 CPPUNIT_ASSERT( !aValues
.empty() );
2117 void DesktopLOKTest::testCommentsWriter()
2119 // Disable tiled rendering for comments
2120 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2122 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2123 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2124 long nWidth
, nHeight
;
2125 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
2127 // Document width alongwith without sidebar comes to be < 13000
2128 CPPUNIT_ASSERT( nWidth
< 13000 );
2130 // Can we get all the comments using .uno:ViewAnnotations command ?
2131 boost::property_tree::ptree aTree
;
2132 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2133 std::stringstream
aStream(pJSON
);
2135 CPPUNIT_ASSERT(!aStream
.str().empty());
2136 boost::property_tree::read_json(aStream
, aTree
);
2137 // There are 3 comments in the document already
2138 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aTree
.get_child("comments").size());
2140 int nComment2Id
= 0;
2141 // Check if all comment fields have valid data
2142 for (const auto& rComment
: aTree
.get_child("comments"))
2144 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2145 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("author").empty());
2146 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("text").empty());
2147 // Has a valid iso 8601 date time string
2148 css::util::DateTime aDateTime
;
2149 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2150 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2152 // This comment has a marked text range
2153 if (rComment
.second
.get
<std::string
>("text") == "Comment 2")
2155 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("textRange").empty());
2156 nComment2Id
= rComment
.second
.get
<int>("id");
2158 // This is a reply comment
2159 else if (rComment
.second
.get
<std::string
>("text") == "Reply to Comment 2")
2161 CPPUNIT_ASSERT_EQUAL(nComment2Id
, rComment
.second
.get
<int>("parent"));
2165 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2169 void DesktopLOKTest::testCommentsCalc()
2171 // Disable tiled rendering for comments
2172 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2174 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2175 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2177 // Can we get all the comments using .uno:ViewAnnotations command ?
2178 boost::property_tree::ptree aTree
;
2179 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2180 std::stringstream
aStream(pJSON
);
2182 CPPUNIT_ASSERT(!aStream
.str().empty());
2183 boost::property_tree::read_json(aStream
, aTree
);
2184 // There are 2 comments in the document already
2185 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2187 // Check if all comment fields have valid data
2189 for (const auto& rComment
: aTree
.get_child("comments"))
2195 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2196 CPPUNIT_ASSERT_EQUAL(std::string("Comment1"), rComment
.second
.get
<std::string
>("text"));
2197 CPPUNIT_ASSERT_EQUAL(std::string("7650, 3570, 1274, 254"), rComment
.second
.get
<std::string
>("cellPos"));
2202 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2203 CPPUNIT_ASSERT_EQUAL(std::string("Comment2"), rComment
.second
.get
<std::string
>("text"));
2204 CPPUNIT_ASSERT_EQUAL(std::string("8925, 4335, 1274, 254"), rComment
.second
.get
<std::string
>("cellPos"));
2212 // We checked all the comments
2213 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2215 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2219 void DesktopLOKTest::testCommentsImpress()
2221 // Disable tiled rendering for comments
2222 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2224 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
2225 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2227 // Can we get all the comments using .uno:ViewAnnotations command ?
2228 boost::property_tree::ptree aTree
;
2229 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2230 std::stringstream
aStream(pJSON
);
2232 CPPUNIT_ASSERT(!aStream
.str().empty());
2233 boost::property_tree::read_json(aStream
, aTree
);
2234 // There are 2 comments in the document already
2235 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2237 // Check if all comment fields have valid data
2239 for (const auto& rComment
: aTree
.get_child("comments"))
2245 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2246 CPPUNIT_ASSERT_EQUAL(std::string("This is comment1"), rComment
.second
.get
<std::string
>("text"));
2247 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), rComment
.second
.get
<std::string
>("author"));
2248 css::util::DateTime aDateTime
;
2249 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2250 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2255 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2256 CPPUNIT_ASSERT_EQUAL(std::string("This is comment2"), rComment
.second
.get
<std::string
>("text"));
2257 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), rComment
.second
.get
<std::string
>("author"));
2258 css::util::DateTime aDateTime
;
2259 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2260 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2268 // We checked all the comments
2269 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2271 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2274 void DesktopLOKTest::testCommentsCallbacksWriter()
2276 // Comments callback are emitted only if tiled annotations are off
2277 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2278 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2279 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2280 ViewCallback
aView1(pDocument
);
2281 pDocument
->m_pDocumentClass
->createView(pDocument
);
2282 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2283 ViewCallback
aView2(pDocument
);
2285 // Add a new comment
2286 OString
aCommandArgs("{ \"Text\": { \"type\": \"string\", \"value\": \"Additional comment\" }, \"Author\": { \"type\": \"string\", \"value\": \"LOK User1\" } }");
2287 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2288 Scheduler::ProcessEventsToIdle();
2290 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2291 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2292 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2293 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2295 // Reply to a comment just added
2296 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment\" } }";
2297 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2298 Scheduler::ProcessEventsToIdle();
2300 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2301 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2302 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2303 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2304 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2305 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2306 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2307 int nCommentId2
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2309 // Edit the previously added comment
2310 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Edited comment\" } }";
2311 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2312 Scheduler::ProcessEventsToIdle();
2314 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2315 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2316 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2317 // parent is unchanged still
2318 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2319 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2320 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2321 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2323 // Delete the reply comment just added
2324 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" } }";
2325 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteComment", aCommandArgs
.getStr(), false);
2326 Scheduler::ProcessEventsToIdle();
2328 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2329 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2330 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2331 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2332 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView2
.m_aCommentCallbackResult
.get
<int>("id"));
2334 // Reply to nCommentId1 again
2335 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment again\" } }";
2336 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2337 Scheduler::ProcessEventsToIdle();
2339 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2340 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2341 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2342 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2343 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2344 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2345 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2347 // .uno:ViewAnnotations returns total of 5 comments
2348 boost::property_tree::ptree aTree
;
2349 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2350 std::stringstream
aStream(pJSON
);
2352 CPPUNIT_ASSERT(!aStream
.str().empty());
2353 boost::property_tree::read_json(aStream
, aTree
);
2354 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aTree
.get_child("comments").size());
2360 void addParameter(tools::JsonWriter
& rJson
, const char* sName
, OString
const & type
, OString
const & value
)
2362 auto testNode
= rJson
.startNode(sName
);
2363 rJson
.put("type", type
);
2364 rJson
.put("value", value
);
2369 void DesktopLOKTest::testCommentsAddEditDeleteDraw()
2371 // Comments callback are emitted only if tiled annotations are off
2372 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2373 LibLODocument_Impl
* pDocument
= loadDoc("BlankDrawDocument.odg");
2374 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2375 ViewCallback
aView1(pDocument
);
2377 // Add a new comment
2378 OString aCommandArgs
;
2380 tools::JsonWriter aJson
;
2381 addParameter(aJson
, "Text", "string", "Comment");
2382 addParameter(aJson
, "Author", "string", "LOK User1");
2383 aCommandArgs
= aJson
.extractAsOString();
2386 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2387 Scheduler::ProcessEventsToIdle();
2389 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2390 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2391 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2393 // Edit the previously added comment
2395 tools::JsonWriter aJson
;
2396 addParameter(aJson
, "Id", "string", OString::number(nCommentId1
));
2397 addParameter(aJson
, "Text", "string", "Edited comment");
2398 aCommandArgs
= aJson
.extractAsOString();
2401 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2402 Scheduler::ProcessEventsToIdle();
2404 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2405 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2406 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2410 tools::JsonWriter aJson
;
2411 addParameter(aJson
, "Id", "string", OString::number(nCommentId1
));
2412 aCommandArgs
= aJson
.extractAsOString();
2414 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteAnnotation", aCommandArgs
.getStr(), false);
2415 Scheduler::ProcessEventsToIdle();
2417 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2418 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2419 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2422 void DesktopLOKTest::testRunMacro()
2424 LibLibreOffice_Impl aOffice
;
2425 bool bGoodMacro
, bNonExistentMacro
;
2427 // Tools macros come pre-installed in system share/basic folder,
2428 bGoodMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, OString("macro:///Tools.Debug.ActivateReadOnlyFlag()").getStr());
2429 CPPUNIT_ASSERT(bGoodMacro
);
2431 bNonExistentMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, OString("macro:///I.Am.Not(There)").getStr());
2432 CPPUNIT_ASSERT(!bNonExistentMacro
);
2435 void DesktopLOKTest::testExtractParameter()
2437 OUString
aOptions("Language=de-DE");
2438 OUString aValue
= extractParameter(aOptions
, "Language");
2439 CPPUNIT_ASSERT_EQUAL(OUString("de-DE"), aValue
);
2440 CPPUNIT_ASSERT_EQUAL(OUString(), aOptions
);
2442 aOptions
= "Language=en-US,Something";
2443 aValue
= extractParameter(aOptions
, "Language");
2444 CPPUNIT_ASSERT_EQUAL(OUString("en-US"), aValue
);
2445 CPPUNIT_ASSERT_EQUAL(OUString("Something"), aOptions
);
2447 aOptions
= "SomethingElse,Language=cs-CZ";
2448 aValue
= extractParameter(aOptions
, "Language");
2449 CPPUNIT_ASSERT_EQUAL(OUString("cs-CZ"), aValue
);
2450 CPPUNIT_ASSERT_EQUAL(OUString("SomethingElse"), aOptions
);
2452 aOptions
= "Something1,Language=hu-HU,Something2";
2453 aValue
= extractParameter(aOptions
, "Language");
2454 CPPUNIT_ASSERT_EQUAL(OUString("hu-HU"), aValue
);
2455 CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2"), aOptions
);
2457 aOptions
= "Something1,Something2=blah,Something3";
2458 aValue
= extractParameter(aOptions
, "Language");
2459 CPPUNIT_ASSERT_EQUAL(OUString(), aValue
);
2460 CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2=blah,Something3"), aOptions
);
2463 void DesktopLOKTest::readFileIntoByteVector(OUString
const & sFilename
, std::vector
<unsigned char> & rByteVector
)
2465 rByteVector
.clear();
2467 createFileURL(sFilename
, aURL
);
2468 SvFileStream
aStream(aURL
, StreamMode::READ
);
2469 rByteVector
.resize(aStream
.remainingSize());
2470 aStream
.ReadBytes(rByteVector
.data(), aStream
.remainingSize());
2473 void DesktopLOKTest::testGetSignatureState_Signed()
2475 LibLODocument_Impl
* pDocument
= loadDoc("signed.odt");
2476 Scheduler::ProcessEventsToIdle();
2477 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2478 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2479 CPPUNIT_ASSERT_EQUAL(int(4), nState
);
2481 std::vector
<unsigned char> aCertificate
;
2483 readFileIntoByteVector("rootCA.der", aCertificate
);
2484 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2485 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2486 CPPUNIT_ASSERT(bResult
);
2490 readFileIntoByteVector("intermediateRootCA.der", aCertificate
);
2491 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2492 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2493 CPPUNIT_ASSERT(bResult
);
2496 nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2497 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2500 void DesktopLOKTest::testGetSignatureState_NonSigned()
2502 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2503 Scheduler::ProcessEventsToIdle();
2504 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2505 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2506 CPPUNIT_ASSERT_EQUAL(int(0), nState
);
2509 void DesktopLOKTest::testInsertCertificate_DER_ODT()
2511 // Load the document, save it into a temp file and load that file again
2512 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2513 utl::TempFile aTempFile
;
2514 aTempFile
.EnableKillingFile();
2515 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2518 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2520 Scheduler::ProcessEventsToIdle();
2521 CPPUNIT_ASSERT(mxComponent
.is());
2522 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2523 Scheduler::ProcessEventsToIdle();
2525 std::vector
<unsigned char> aCertificate
;
2526 std::vector
<unsigned char> aPrivateKey
;
2529 readFileIntoByteVector("rootCA.der", aCertificate
);
2531 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2532 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2533 CPPUNIT_ASSERT(bResult
);
2537 readFileIntoByteVector("intermediateRootCA.der", aCertificate
);
2539 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2540 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2541 CPPUNIT_ASSERT(bResult
);
2545 readFileIntoByteVector("certificate.der", aCertificate
);
2546 readFileIntoByteVector("certificatePrivateKey.der", aPrivateKey
);
2548 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2549 aCertificate
.data(), int(aCertificate
.size()),
2550 aPrivateKey
.data(), int(aPrivateKey
.size()));
2551 CPPUNIT_ASSERT(bResult
);
2554 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2555 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2559 void DesktopLOKTest::testInsertCertificate_PEM_ODT()
2561 // Load the document, save it into a temp file and load that file again
2562 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2563 utl::TempFile aTempFile
;
2564 aTempFile
.EnableKillingFile();
2565 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2568 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2570 Scheduler::ProcessEventsToIdle();
2571 CPPUNIT_ASSERT(mxComponent
.is());
2572 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2573 Scheduler::ProcessEventsToIdle();
2575 std::vector
<unsigned char> aCertificate
;
2576 std::vector
<unsigned char> aPrivateKey
;
2579 readFileIntoByteVector("test-cert-chain-1.pem", aCertificate
);
2581 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2582 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2583 CPPUNIT_ASSERT(bResult
);
2587 readFileIntoByteVector("test-cert-chain-2.pem", aCertificate
);
2589 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2590 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2591 CPPUNIT_ASSERT(bResult
);
2595 readFileIntoByteVector("test-cert-chain-3.pem", aCertificate
);
2597 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2598 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2599 CPPUNIT_ASSERT(bResult
);
2603 readFileIntoByteVector("test-cert-signing.pem", aCertificate
);
2604 readFileIntoByteVector("test-PK-signing.pem", aPrivateKey
);
2606 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2607 aCertificate
.data(), int(aCertificate
.size()),
2608 aPrivateKey
.data(), int(aPrivateKey
.size()));
2609 CPPUNIT_ASSERT(bResult
);
2612 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2613 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2616 void DesktopLOKTest::testInsertCertificate_PEM_DOCX()
2618 // Load the document, save it into a temp file and load that file again
2619 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.docx");
2620 utl::TempFile aTempFile
;
2621 aTempFile
.EnableKillingFile();
2622 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "docx", nullptr));
2625 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2627 Scheduler::ProcessEventsToIdle();
2628 CPPUNIT_ASSERT(mxComponent
.is());
2629 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2630 Scheduler::ProcessEventsToIdle();
2632 std::vector
<unsigned char> aCertificate
;
2633 std::vector
<unsigned char> aPrivateKey
;
2636 readFileIntoByteVector("test-cert-chain-1.pem", aCertificate
);
2638 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2639 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2640 CPPUNIT_ASSERT(bResult
);
2644 readFileIntoByteVector("test-cert-chain-2.pem", aCertificate
);
2646 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2647 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2648 CPPUNIT_ASSERT(bResult
);
2652 readFileIntoByteVector("test-cert-chain-3.pem", aCertificate
);
2654 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2655 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2656 CPPUNIT_ASSERT(bResult
);
2660 readFileIntoByteVector("test-cert-signing.pem", aCertificate
);
2661 readFileIntoByteVector("test-PK-signing.pem", aPrivateKey
);
2663 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2664 aCertificate
.data(), int(aCertificate
.size()),
2665 aPrivateKey
.data(), int(aPrivateKey
.size()));
2666 CPPUNIT_ASSERT(bResult
);
2669 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2670 CPPUNIT_ASSERT_EQUAL(int(5), nState
);
2673 void DesktopLOKTest::testSignDocument_PEM_PDF()
2675 // Load the document, save it into a temp file and load that file again
2676 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2677 utl::TempFile aTempFile
;
2678 aTempFile
.EnableKillingFile();
2680 Scheduler::ProcessEventsToIdle();
2681 CPPUNIT_ASSERT(mxComponent
.is());
2682 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2683 Scheduler::ProcessEventsToIdle();
2685 std::vector
<unsigned char> aCertificate
;
2686 std::vector
<unsigned char> aPrivateKey
;
2689 readFileIntoByteVector("test-cert-chain-1.pem", aCertificate
);
2691 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2692 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2693 CPPUNIT_ASSERT(bResult
);
2697 readFileIntoByteVector("test-cert-chain-2.pem", aCertificate
);
2699 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2700 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2701 CPPUNIT_ASSERT(bResult
);
2705 readFileIntoByteVector("test-cert-chain-3.pem", aCertificate
);
2707 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2708 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2709 CPPUNIT_ASSERT(bResult
);
2712 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "pdf", nullptr));
2716 Scheduler::ProcessEventsToIdle();
2718 readFileIntoByteVector("test-cert-signing.pem", aCertificate
);
2719 readFileIntoByteVector("test-PK-signing.pem", aPrivateKey
);
2721 LibLibreOffice_Impl aOffice
;
2722 bool bResult
= aOffice
.m_pOfficeClass
->signDocument(&aOffice
, aTempFile
.GetURL().toUtf8().getStr(),
2723 aCertificate
.data(), int(aCertificate
.size()),
2724 aPrivateKey
.data(), int(aPrivateKey
.size()));
2726 CPPUNIT_ASSERT(bResult
);
2729 void DesktopLOKTest::testTextSelectionHandles()
2731 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2732 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
2734 OString
aText("hello");
2735 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
2737 // select the inserted text
2738 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2739 Scheduler::ProcessEventsToIdle();
2740 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2741 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2743 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart
);
2744 CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd
);
2747 m_aTextSelectionStart
= "";
2748 m_aTextSelectionEnd
= "";
2749 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
2750 Scheduler::ProcessEventsToIdle();
2751 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2752 CPPUNIT_ASSERT_EQUAL(OString(), OString(pText
));
2754 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionStart
);
2755 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionEnd
);
2757 // select again; the positions of the selection handles have to be sent
2759 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2760 Scheduler::ProcessEventsToIdle();
2761 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2762 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2764 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart
);
2765 CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd
);
2768 void DesktopLOKTest::testDialogPaste()
2770 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2771 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
2772 Scheduler::ProcessEventsToIdle();
2774 SfxViewShell
* pViewShell
= SfxViewShell::Current();
2775 pViewShell
->GetViewFrame()->GetBindings().Update();
2777 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
2778 CPPUNIT_ASSERT(pWindow
);
2780 pDocument
->pClass
->postWindow(pDocument
, pWindow
->GetLOKWindowId(), LOK_WINDOW_PASTE
,
2781 "{ \"MimeType\" : { \"type\" : \"string\", \"value\" : \"text/plain;charset=utf-8\" }, \"Data\" : { \"type\" : \"[]byte\", \"value\" : \"www.softwarelibre.org.bo\" } }");
2782 Scheduler::ProcessEventsToIdle();
2784 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
2785 CPPUNIT_ASSERT(pCtrlFocused
);
2786 ComboBox
* pCtrlURL
= dynamic_cast<ComboBox
*>(pCtrlFocused
);
2787 CPPUNIT_ASSERT(pCtrlURL
);
2788 CPPUNIT_ASSERT_EQUAL(OUString("www.softwarelibre.org.bo"), pCtrlURL
->GetText());
2790 static_cast<SystemWindow
*>(pWindow
.get())->Close();
2791 Scheduler::ProcessEventsToIdle();
2794 void DesktopLOKTest::testShowHideDialog()
2797 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2799 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2800 ViewCallback
aView(pDocument
);
2802 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
2803 Scheduler::ProcessEventsToIdle();
2805 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
2806 CPPUNIT_ASSERT(pWindow
);
2809 Scheduler::ProcessEventsToIdle();
2811 CPPUNIT_ASSERT_EQUAL(std::string("hide"), aView
.m_aCallbackWindowResult
.get
<std::string
>("action"));
2814 Scheduler::ProcessEventsToIdle();
2816 CPPUNIT_ASSERT_EQUAL(std::string("invalidate"), aView
.m_aCallbackWindowResult
.get
<std::string
>("action"));
2819 void DesktopLOKTest::testComplexSelection()
2821 // Start with a blank text file and add contents.
2822 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2823 static const OString
aText("hello world");
2825 // Certainly not complex.
2826 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
2829 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
2832 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
2836 createFileURL("paste.jpg", aFileURL
);
2837 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
2838 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
2839 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
2842 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2843 Scheduler::ProcessEventsToIdle();
2845 // Export as plain text, we should get only the text part "hello".
2846 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2847 CPPUNIT_ASSERT(pText
!= nullptr);
2848 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2851 // Export as rtf, we should also get the image.
2852 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/rtf", nullptr);
2853 CPPUNIT_ASSERT(pText
!= nullptr);
2854 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
2855 CPPUNIT_ASSERT(std::string(pText
).find("pict{") != std::string::npos
); // Must have the image as well.
2858 // Export as html, we should also get the image.
2859 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/html", nullptr);
2860 CPPUNIT_ASSERT(pText
!= nullptr);
2861 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
2862 CPPUNIT_ASSERT(std::string(pText
).find("<img") != std::string::npos
); // Must have the image as well.
2865 // We expect this to be complex.
2866 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_COMPLEX
), pDocument
->pClass
->getSelectionType(pDocument
));
2869 void DesktopLOKTest::testCalcSaveAs()
2871 comphelper::LibreOfficeKit::setActive();
2873 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2874 CPPUNIT_ASSERT(pDocument
);
2876 // Enter some text, but don't commit.
2877 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'X', 0);
2878 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'X', 0);
2879 Scheduler::ProcessEventsToIdle();
2881 // Save as a new file.
2882 OUString aNewFileUrl
= "file:///tmp/saveas.ods";
2883 pDocument
->pClass
->saveAs(pDocument
, aNewFileUrl
.toUtf8().getStr(), nullptr, nullptr);
2886 // Load the new document and verify that the in-flight changes are saved.
2887 pDocument
= loadDocUrl(aNewFileUrl
, LOK_DOCTYPE_SPREADSHEET
);
2888 CPPUNIT_ASSERT(pDocument
);
2890 ViewCallback
aView(pDocument
);
2891 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2892 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView
);
2894 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RIGHT
);
2895 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RIGHT
);
2896 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_LEFT
);
2897 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_LEFT
);
2898 Scheduler::ProcessEventsToIdle();
2900 CPPUNIT_ASSERT_EQUAL(OString("X"), aView
.m_aCellFormula
);
2903 void DesktopLOKTest::testControlState()
2905 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
2906 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:StarShapes", nullptr, false);
2907 Scheduler::ProcessEventsToIdle();
2909 boost::property_tree::ptree aState
;
2910 SfxViewShell
* pViewShell
= SfxViewShell::Current();
2911 pViewShell
->GetViewFrame()->GetBindings().Update();
2912 pViewShell
->GetViewFrame()->GetBindings().QueryControlState(SID_ATTR_TRANSFORM_WIDTH
, aState
);
2913 CPPUNIT_ASSERT(!aState
.empty());
2916 void DesktopLOKTest::testMetricField()
2918 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
2919 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:StarShapes", nullptr, false);
2920 Scheduler::ProcessEventsToIdle();
2922 SfxViewShell
* pViewShell
= SfxViewShell::Current();
2923 CPPUNIT_ASSERT(pViewShell
);
2925 SfxViewFrame
* pViewFrame
= pViewShell
->GetViewFrame();
2926 CPPUNIT_ASSERT(pViewFrame
);
2928 SfxChildWindow
* pSideBar
= pViewFrame
->GetChildWindow(SID_SIDEBAR
);
2929 CPPUNIT_ASSERT(pSideBar
);
2931 vcl::Window
* pWin
= pSideBar
->GetWindow();
2932 CPPUNIT_ASSERT(pWin
);
2934 WindowUIObject
aWinUI(pWin
);
2935 std::unique_ptr
<UIObject
> pUIWin(aWinUI
.get_child("selectwidth"));
2936 CPPUNIT_ASSERT(pUIWin
.get());
2939 aMap
["VALUE"] = "75.06";
2940 pUIWin
->execute("VALUE", aMap
);
2942 StringMap aRet
= pUIWin
->get_state();
2943 CPPUNIT_ASSERT_EQUAL(aMap
["VALUE"], aRet
["Value"]);
2946 void DesktopLOKTest::testSpellcheckerMultiView()
2948 static const OUString
aLangISO("en-US");
2949 SvtSysLocaleOptions aSysLocaleOptions
;
2950 aSysLocaleOptions
.SetLocaleConfigString(aLangISO
);
2951 aSysLocaleOptions
.SetUILocaleConfigString(aLangISO
);
2952 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLangISO
, true));
2954 auto aSavedSettings
= Application::GetSettings();
2955 std::unique_ptr
<Resetter
> pResetter(
2956 new Resetter([&]() { Application::SetSettings(aSavedSettings
); }));
2957 AllSettings
aSettings(aSavedSettings
);
2958 aSettings
.SetLanguageTag(aLangISO
, true);
2959 Application::SetSettings(aSettings
);
2961 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
2962 pDocument
->pClass
->setViewLanguage(pDocument
, 0, "en-US"); // For spellchecking.
2963 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
2964 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
2966 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2967 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2968 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2969 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
2971 // Start spellchecking.
2972 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SpellDialog", nullptr, false);
2974 // Uncommenting this will result in a deadlock.
2975 // Because the language configuration above is not effective, and no
2976 // language is actually set, the spell-dialog finds no misspelled
2977 // words, and displays a message box, which must be dismissed to
2979 // Need to fix the language configuration issue to enable this.
2980 // Scheduler::ProcessEventsToIdle();
2982 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2984 // Now create another view.
2985 const int nViewId
= pDocument
->m_pDocumentClass
->createView(pDocument
);
2986 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2989 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nViewId
);
2991 // We should survive the destroyed view.
2992 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2995 void DesktopLOKTest::testMultiDocuments()
2997 for (int i
= 0; i
< 3; i
++)
3000 uno::Reference
<lang::XComponent
> xComponent1
;
3001 std::unique_ptr
<LibLODocument_Impl
> document1
;
3002 std::tie(document1
, xComponent1
) = loadDocImpl("blank_text.odt");
3003 LibLODocument_Impl
* pDocument1
= document1
.get();
3004 CPPUNIT_ASSERT_EQUAL(1, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3005 const int nDocId1
= pDocument1
->mnDocumentId
;
3007 const int nDoc1View0
= pDocument1
->m_pDocumentClass
->getView(pDocument1
);
3008 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3009 const int nDoc1View1
= pDocument1
->m_pDocumentClass
->createView(pDocument1
);
3010 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3011 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3012 CPPUNIT_ASSERT_EQUAL(2, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3014 // Validate the views of document 1.
3015 std::vector
<int> aViewIdsDoc1(2);
3016 CPPUNIT_ASSERT(pDocument1
->m_pDocumentClass
->getViewIds(pDocument1
, aViewIdsDoc1
.data(), aViewIdsDoc1
.size()));
3017 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, aViewIdsDoc1
[0]);
3018 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, aViewIdsDoc1
[1]);
3020 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3021 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3022 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View0
);
3023 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3024 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3025 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View1
);
3026 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3027 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3028 CPPUNIT_ASSERT_EQUAL(2, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3030 // Load another document.
3031 uno::Reference
<lang::XComponent
> xComponent2
;
3032 std::unique_ptr
<LibLODocument_Impl
> document2
;
3033 std::tie(document2
, xComponent2
) = loadDocImpl("blank_presentation.odp");
3034 LibLODocument_Impl
* pDocument2
= document2
.get();
3035 CPPUNIT_ASSERT_EQUAL(1, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3036 const int nDocId2
= pDocument2
->mnDocumentId
;
3038 const int nDoc2View0
= pDocument2
->m_pDocumentClass
->getView(pDocument2
);
3039 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3040 const int nDoc2View1
= pDocument2
->m_pDocumentClass
->createView(pDocument2
);
3041 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3042 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3043 CPPUNIT_ASSERT_EQUAL(2, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3045 // Validate the views of document 2.
3046 std::vector
<int> aViewIdsDoc2(2);
3047 CPPUNIT_ASSERT(pDocument2
->m_pDocumentClass
->getViewIds(pDocument2
, aViewIdsDoc2
.data(), aViewIdsDoc2
.size()));
3048 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, aViewIdsDoc2
[0]);
3049 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, aViewIdsDoc2
[1]);
3051 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3052 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3053 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View0
);
3054 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3055 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3056 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View1
);
3057 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3058 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3059 CPPUNIT_ASSERT_EQUAL(2, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3061 // The views of document1 should be unchanged.
3062 CPPUNIT_ASSERT(pDocument1
->m_pDocumentClass
->getViewIds(pDocument1
, aViewIdsDoc1
.data(), aViewIdsDoc1
.size()));
3063 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, aViewIdsDoc1
[0]);
3064 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, aViewIdsDoc1
[1]);
3065 // Switch views in the first doc.
3066 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3067 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View0
);
3068 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3069 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3070 pDocument1
->m_pDocumentClass
->destroyView(pDocument1
, nDoc1View1
);
3071 CPPUNIT_ASSERT_EQUAL(1, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3073 // The views of document2 should be unchanged.
3074 CPPUNIT_ASSERT(pDocument2
->m_pDocumentClass
->getViewIds(pDocument2
, aViewIdsDoc2
.data(), aViewIdsDoc2
.size()));
3075 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, aViewIdsDoc2
[0]);
3076 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, aViewIdsDoc2
[1]);
3077 // Switch views in the second doc.
3078 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3079 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View0
);
3080 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3081 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3082 pDocument2
->m_pDocumentClass
->destroyView(pDocument2
, nDoc2View1
);
3083 CPPUNIT_ASSERT_EQUAL(1, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3085 closeDoc(document2
, xComponent2
);
3087 closeDoc(document1
, xComponent1
);
3093 constexpr size_t classOffset(int i
)
3095 return sizeof(static_cast<struct _LibreOfficeKitClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
3098 constexpr size_t documentClassOffset(int i
)
3100 return sizeof(static_cast<struct _LibreOfficeKitDocumentClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
3105 void DesktopLOKTest::testABI()
3107 // STABLE ABI, NEVER CHANGE (unless there's a very good reason, agreed by ESC, etc.)
3108 CPPUNIT_ASSERT_EQUAL(classOffset(0), offsetof(struct _LibreOfficeKitClass
, destroy
));
3109 CPPUNIT_ASSERT_EQUAL(classOffset(1), offsetof(struct _LibreOfficeKitClass
, documentLoad
));
3110 CPPUNIT_ASSERT_EQUAL(classOffset(2), offsetof(struct _LibreOfficeKitClass
, getError
));
3111 CPPUNIT_ASSERT_EQUAL(classOffset(3), offsetof(struct _LibreOfficeKitClass
, documentLoadWithOptions
));
3112 CPPUNIT_ASSERT_EQUAL(classOffset(4), offsetof(struct _LibreOfficeKitClass
, freeError
));
3113 CPPUNIT_ASSERT_EQUAL(classOffset(5), offsetof(struct _LibreOfficeKitClass
, registerCallback
));
3114 CPPUNIT_ASSERT_EQUAL(classOffset(6), offsetof(struct _LibreOfficeKitClass
, getFilterTypes
));
3115 CPPUNIT_ASSERT_EQUAL(classOffset(7), offsetof(struct _LibreOfficeKitClass
, setOptionalFeatures
));
3116 CPPUNIT_ASSERT_EQUAL(classOffset(8), offsetof(struct _LibreOfficeKitClass
, setDocumentPassword
));
3117 CPPUNIT_ASSERT_EQUAL(classOffset(9), offsetof(struct _LibreOfficeKitClass
, getVersionInfo
));
3118 CPPUNIT_ASSERT_EQUAL(classOffset(10), offsetof(struct _LibreOfficeKitClass
, runMacro
));
3119 CPPUNIT_ASSERT_EQUAL(classOffset(11), offsetof(struct _LibreOfficeKitClass
, signDocument
));
3121 CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass
, destroy
));
3122 CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass
, saveAs
));
3124 // Unstable ABI, but still think twice before changing this
3125 // Eg. can't you add your new member at the end of the struct instead of
3126 // in the middle? The thing you are changing - is it already part of some
3128 CPPUNIT_ASSERT_EQUAL(documentClassOffset(2), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentType
));
3129 CPPUNIT_ASSERT_EQUAL(documentClassOffset(3), offsetof(struct _LibreOfficeKitDocumentClass
, getParts
));
3130 CPPUNIT_ASSERT_EQUAL(documentClassOffset(4), offsetof(struct _LibreOfficeKitDocumentClass
, getPartPageRectangles
));
3131 CPPUNIT_ASSERT_EQUAL(documentClassOffset(5), offsetof(struct _LibreOfficeKitDocumentClass
, getPart
));
3132 CPPUNIT_ASSERT_EQUAL(documentClassOffset(6), offsetof(struct _LibreOfficeKitDocumentClass
, setPart
));
3133 CPPUNIT_ASSERT_EQUAL(documentClassOffset(7), offsetof(struct _LibreOfficeKitDocumentClass
, getPartName
));
3134 CPPUNIT_ASSERT_EQUAL(documentClassOffset(8), offsetof(struct _LibreOfficeKitDocumentClass
, setPartMode
));
3135 CPPUNIT_ASSERT_EQUAL(documentClassOffset(9), offsetof(struct _LibreOfficeKitDocumentClass
, paintTile
));
3136 CPPUNIT_ASSERT_EQUAL(documentClassOffset(10), offsetof(struct _LibreOfficeKitDocumentClass
, getTileMode
));
3137 CPPUNIT_ASSERT_EQUAL(documentClassOffset(11), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentSize
));
3138 CPPUNIT_ASSERT_EQUAL(documentClassOffset(12), offsetof(struct _LibreOfficeKitDocumentClass
, initializeForRendering
));
3139 CPPUNIT_ASSERT_EQUAL(documentClassOffset(13), offsetof(struct _LibreOfficeKitDocumentClass
, registerCallback
));
3140 CPPUNIT_ASSERT_EQUAL(documentClassOffset(14), offsetof(struct _LibreOfficeKitDocumentClass
, postKeyEvent
));
3141 CPPUNIT_ASSERT_EQUAL(documentClassOffset(15), offsetof(struct _LibreOfficeKitDocumentClass
, postMouseEvent
));
3142 CPPUNIT_ASSERT_EQUAL(documentClassOffset(16), offsetof(struct _LibreOfficeKitDocumentClass
, postUnoCommand
));
3143 CPPUNIT_ASSERT_EQUAL(documentClassOffset(17), offsetof(struct _LibreOfficeKitDocumentClass
, setTextSelection
));
3144 CPPUNIT_ASSERT_EQUAL(documentClassOffset(18), offsetof(struct _LibreOfficeKitDocumentClass
, getTextSelection
));
3145 CPPUNIT_ASSERT_EQUAL(documentClassOffset(19), offsetof(struct _LibreOfficeKitDocumentClass
, paste
));
3146 CPPUNIT_ASSERT_EQUAL(documentClassOffset(20), offsetof(struct _LibreOfficeKitDocumentClass
, setGraphicSelection
));
3147 CPPUNIT_ASSERT_EQUAL(documentClassOffset(21), offsetof(struct _LibreOfficeKitDocumentClass
, resetSelection
));
3148 CPPUNIT_ASSERT_EQUAL(documentClassOffset(22), offsetof(struct _LibreOfficeKitDocumentClass
, getCommandValues
));
3149 CPPUNIT_ASSERT_EQUAL(documentClassOffset(23), offsetof(struct _LibreOfficeKitDocumentClass
, setClientZoom
));
3150 CPPUNIT_ASSERT_EQUAL(documentClassOffset(24), offsetof(struct _LibreOfficeKitDocumentClass
, setClientVisibleArea
));
3151 CPPUNIT_ASSERT_EQUAL(documentClassOffset(25), offsetof(struct _LibreOfficeKitDocumentClass
, createView
));
3152 CPPUNIT_ASSERT_EQUAL(documentClassOffset(26), offsetof(struct _LibreOfficeKitDocumentClass
, destroyView
));
3153 CPPUNIT_ASSERT_EQUAL(documentClassOffset(27), offsetof(struct _LibreOfficeKitDocumentClass
, setView
));
3154 CPPUNIT_ASSERT_EQUAL(documentClassOffset(28), offsetof(struct _LibreOfficeKitDocumentClass
, getView
));
3155 CPPUNIT_ASSERT_EQUAL(documentClassOffset(29), offsetof(struct _LibreOfficeKitDocumentClass
, getViewsCount
));
3156 CPPUNIT_ASSERT_EQUAL(documentClassOffset(30), offsetof(struct _LibreOfficeKitDocumentClass
, renderFont
));
3157 CPPUNIT_ASSERT_EQUAL(documentClassOffset(31), offsetof(struct _LibreOfficeKitDocumentClass
, getPartHash
));
3158 CPPUNIT_ASSERT_EQUAL(documentClassOffset(32), offsetof(struct _LibreOfficeKitDocumentClass
, paintPartTile
));
3159 CPPUNIT_ASSERT_EQUAL(documentClassOffset(33), offsetof(struct _LibreOfficeKitDocumentClass
, getViewIds
));
3160 CPPUNIT_ASSERT_EQUAL(documentClassOffset(34), offsetof(struct _LibreOfficeKitDocumentClass
, setOutlineState
));
3161 CPPUNIT_ASSERT_EQUAL(documentClassOffset(35), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindow
));
3162 CPPUNIT_ASSERT_EQUAL(documentClassOffset(36), offsetof(struct _LibreOfficeKitDocumentClass
, postWindow
));
3163 CPPUNIT_ASSERT_EQUAL(documentClassOffset(37), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowKeyEvent
));
3164 CPPUNIT_ASSERT_EQUAL(documentClassOffset(38), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowMouseEvent
));
3165 CPPUNIT_ASSERT_EQUAL(documentClassOffset(39), offsetof(struct _LibreOfficeKitDocumentClass
, setViewLanguage
));
3166 CPPUNIT_ASSERT_EQUAL(documentClassOffset(40), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowExtTextInputEvent
));
3167 CPPUNIT_ASSERT_EQUAL(documentClassOffset(41), offsetof(struct _LibreOfficeKitDocumentClass
, getPartInfo
));
3168 CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowDPI
));
3169 CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass
, insertCertificate
));
3170 CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), offsetof(struct _LibreOfficeKitDocumentClass
, addCertificate
));
3171 CPPUNIT_ASSERT_EQUAL(documentClassOffset(45), offsetof(struct _LibreOfficeKitDocumentClass
, getSignatureState
));
3172 CPPUNIT_ASSERT_EQUAL(documentClassOffset(46), offsetof(struct _LibreOfficeKitDocumentClass
, renderShapeSelection
));
3173 CPPUNIT_ASSERT_EQUAL(documentClassOffset(47), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowGestureEvent
));
3174 CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass
, createViewWithOptions
));
3175 CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass
, selectPart
));
3176 CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass
, moveSelectedParts
));
3177 CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass
, resizeWindow
));
3178 CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass
, getClipboard
));
3179 CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass
, setClipboard
));
3180 CPPUNIT_ASSERT_EQUAL(documentClassOffset(54), offsetof(struct _LibreOfficeKitDocumentClass
, getSelectionType
));
3181 CPPUNIT_ASSERT_EQUAL(documentClassOffset(55), offsetof(struct _LibreOfficeKitDocumentClass
, removeTextContext
));
3182 CPPUNIT_ASSERT_EQUAL(documentClassOffset(56), offsetof(struct _LibreOfficeKitDocumentClass
, sendDialogEvent
));
3183 CPPUNIT_ASSERT_EQUAL(documentClassOffset(57), offsetof(struct _LibreOfficeKitDocumentClass
, renderFontOrientation
));
3184 CPPUNIT_ASSERT_EQUAL(documentClassOffset(58), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowForView
));
3185 CPPUNIT_ASSERT_EQUAL(documentClassOffset(59), offsetof(struct _LibreOfficeKitDocumentClass
, completeFunction
));
3186 CPPUNIT_ASSERT_EQUAL(documentClassOffset(60), offsetof(struct _LibreOfficeKitDocumentClass
, setWindowTextSelection
));
3187 CPPUNIT_ASSERT_EQUAL(documentClassOffset(61), offsetof(struct _LibreOfficeKitDocumentClass
, sendFormFieldEvent
));
3189 // Extending is fine, update this, and add new assert for the offsetof the
3191 CPPUNIT_ASSERT_EQUAL(documentClassOffset(62), sizeof(struct _LibreOfficeKitDocumentClass
));
3194 CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest
);
3196 CPPUNIT_PLUGIN_IMPLEMENT();
3198 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */