1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <config_oox.h>
12 #include <string_view>
14 #include <com/sun/star/frame/Desktop.hpp>
15 #include <com/sun/star/lang/XComponent.hpp>
16 #include <com/sun/star/text/XTextDocument.hpp>
17 #include <com/sun/star/awt/Key.hpp>
18 #include <com/sun/star/awt/XReschedule.hpp>
19 #include <com/sun/star/awt/Toolkit.hpp>
20 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
21 #include <com/sun/star/text/TextContentAnchorType.hpp>
22 #include <boost/property_tree/json_parser.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <vcl/scheduler.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/syswin.hxx>
28 #include <vcl/window.hxx>
29 #include <vcl/ctrl.hxx>
30 #include <vcl/uitest/uiobject.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <rtl/math.hxx>
33 #include <rtl/uri.hxx>
34 #include <sfx2/app.hxx>
35 #include <sfx2/childwin.hxx>
36 #include <sfx2/lokhelper.hxx>
37 #include <test/unoapi_test.hxx>
38 #include <comphelper/lok.hxx>
39 #include <comphelper/dispatchcommand.hxx>
40 #include <comphelper/propertysequence.hxx>
41 #include <osl/conditn.hxx>
42 #include <osl/thread.hxx>
43 #include <svl/srchitem.hxx>
44 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
45 #include <unotools/tempfile.hxx>
46 #include <sfx2/viewsh.hxx>
47 #include <sfx2/viewfrm.hxx>
48 #include <sfx2/bindings.hxx>
49 #include <unotools/datetime.hxx>
50 #include <unotools/syslocaleoptions.hxx>
51 #include <comphelper/string.hxx>
52 #include <comphelper/scopeguard.hxx>
54 #include <config_features.h>
55 #include <config_mpl.h>
56 #include <tools/json_writer.hxx>
58 #include <lib/init.hxx>
59 #include <svx/svxids.hrc>
61 #include <cppunit/TestAssert.h>
67 using namespace com::sun::star
;
68 using namespace desktop
;
70 static LibreOfficeKitDocumentType
getDocumentTypeFromName(const char* pName
)
72 CPPUNIT_ASSERT_MESSAGE("Document name must be valid.", pName
!= nullptr);
74 const std::string
name(pName
);
75 CPPUNIT_ASSERT_MESSAGE("Document name must include extension.", name
.size() > 4);
77 const auto it
= name
.rfind('.');
78 if (it
!= std::string::npos
)
80 const std::string ext
= name
.substr(it
);
83 return LOK_DOCTYPE_SPREADSHEET
;
86 return LOK_DOCTYPE_PRESENTATION
;
89 CPPUNIT_ASSERT_MESSAGE("Document name must include extension.", it
!= std::string::npos
);
90 return LOK_DOCTYPE_TEXT
;
93 class DesktopLOKTest
: public UnoApiTest
96 DesktopLOKTest() : UnoApiTest("/desktop/qa/data/"),
97 m_nSelectionBeforeSearchResult(0),
98 m_nSelectionAfterSearchResult(0),
105 void readFileIntoByteVector(
106 std::u16string_view sFilename
, std::vector
<sal_uInt8
> & rByteVector
);
108 virtual void setUp() override
110 comphelper::LibreOfficeKit::setActive(true);
113 mxDesktop
.set(frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
114 SfxApplication::GetOrCreate();
117 virtual void tearDown() override
121 UnoApiTest::tearDown();
123 comphelper::LibreOfficeKit::setActive(false);
126 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
127 loadDocImpl(const char* pName
, LibreOfficeKitDocumentType eType
);
130 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
131 loadDocImpl(const char* pName
);
134 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
135 loadDocUrlImpl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
137 LibLODocument_Impl
* loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
);
138 LibLODocument_Impl
* loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
);
139 LibLODocument_Impl
* loadDoc(const char* pName
)
141 return loadDoc(pName
, getDocumentTypeFromName(pName
));
144 void closeDoc(std::unique_ptr
<LibLODocument_Impl
>& loDocument
,
145 uno::Reference
<lang::XComponent
>& xComponent
);
146 void closeDoc() { closeDoc(m_pDocument
, mxComponent
); }
147 static void callback(int nType
, const char* pPayload
, void* pData
);
148 void callbackImpl(int nType
, const char* pPayload
);
150 void testGetStyles();
152 void testCreateView();
153 void testGetFilterTypes();
154 void testGetPartPageRectangles();
155 void testSearchCalc();
156 void testSearchAllNotificationsCalc();
157 void testPaintTile();
159 void testSaveAsCalc();
160 void testPasteWriter();
161 void testPasteWriterJPEG();
162 void testUndoWriter();
163 void testRowColumnHeaders();
164 void testHiddenRowHeaders();
165 void testCellCursor();
166 void testCommandResult();
167 void testWriterComments();
168 void testSheetOperations();
169 void testSheetSelections();
170 void testContextMenuCalc();
171 void testContextMenuWriter();
172 void testContextMenuImpress();
173 void testNotificationCompression();
174 void testTileInvalidationCompression();
175 void testPartInInvalidation();
177 void testRedlineWriter();
178 void testTrackChanges();
179 void testRedlineCalc();
180 void testPaintPartTile();
182 void testGetFontSubset();
184 void testCommentsWriter();
185 void testCommentsCalc();
186 void testCommentsImpress();
187 void testCommentsCallbacksWriter();
188 void testCommentsAddEditDeleteDraw();
190 void testExtractParameter();
191 void testGetSignatureState_NonSigned();
192 void testGetSignatureState_Signed();
193 void testInsertCertificate_DER_ODT();
194 void testInsertCertificate_PEM_ODT();
195 void testInsertCertificate_PEM_DOCX();
196 void testSignDocument_PEM_PDF();
197 void testTextSelectionHandles();
198 void testComplexSelection();
199 void testSpellcheckerMultiView();
200 void testDialogPaste();
201 void testShowHideDialog();
202 void testDialogInput();
203 void testCalcSaveAs();
204 void testControlState();
205 void testMetricField();
206 void testMultiDocuments();
207 void testJumpCursor();
210 CPPUNIT_TEST_SUITE(DesktopLOKTest
);
211 CPPUNIT_TEST(testGetStyles
);
212 CPPUNIT_TEST(testGetFonts
);
213 CPPUNIT_TEST(testCreateView
);
214 CPPUNIT_TEST(testGetFilterTypes
);
215 CPPUNIT_TEST(testGetPartPageRectangles
);
216 CPPUNIT_TEST(testSearchCalc
);
217 CPPUNIT_TEST(testSearchAllNotificationsCalc
);
218 CPPUNIT_TEST(testPaintTile
);
219 CPPUNIT_TEST(testSaveAs
);
220 CPPUNIT_TEST(testSaveAsCalc
);
221 CPPUNIT_TEST(testPasteWriter
);
222 CPPUNIT_TEST(testPasteWriterJPEG
);
223 CPPUNIT_TEST(testUndoWriter
);
224 CPPUNIT_TEST(testRowColumnHeaders
);
225 CPPUNIT_TEST(testHiddenRowHeaders
);
226 CPPUNIT_TEST(testCellCursor
);
227 CPPUNIT_TEST(testCommandResult
);
228 CPPUNIT_TEST(testWriterComments
);
229 CPPUNIT_TEST(testSheetOperations
);
230 CPPUNIT_TEST(testSheetSelections
);
231 CPPUNIT_TEST(testContextMenuCalc
);
232 CPPUNIT_TEST(testContextMenuWriter
);
233 CPPUNIT_TEST(testContextMenuImpress
);
234 CPPUNIT_TEST(testNotificationCompression
);
235 CPPUNIT_TEST(testTileInvalidationCompression
);
236 CPPUNIT_TEST(testPartInInvalidation
);
237 CPPUNIT_TEST(testInput
);
238 CPPUNIT_TEST(testRedlineWriter
);
239 CPPUNIT_TEST(testTrackChanges
);
240 CPPUNIT_TEST(testRedlineCalc
);
241 CPPUNIT_TEST(testPaintPartTile
);
243 CPPUNIT_TEST(testGetFontSubset
);
245 CPPUNIT_TEST(testCommentsWriter
);
246 CPPUNIT_TEST(testCommentsCalc
);
247 CPPUNIT_TEST(testCommentsImpress
);
248 CPPUNIT_TEST(testCommentsCallbacksWriter
);
249 CPPUNIT_TEST(testCommentsAddEditDeleteDraw
);
250 CPPUNIT_TEST(testRunMacro
);
251 CPPUNIT_TEST(testExtractParameter
);
252 CPPUNIT_TEST(testGetSignatureState_Signed
);
253 CPPUNIT_TEST(testGetSignatureState_NonSigned
);
255 CPPUNIT_TEST(testInsertCertificate_DER_ODT
);
256 CPPUNIT_TEST(testInsertCertificate_PEM_ODT
);
257 CPPUNIT_TEST(testInsertCertificate_PEM_DOCX
);
258 CPPUNIT_TEST(testSignDocument_PEM_PDF
);
260 CPPUNIT_TEST(testTextSelectionHandles
);
261 CPPUNIT_TEST(testComplexSelection
);
262 CPPUNIT_TEST(testSpellcheckerMultiView
);
263 CPPUNIT_TEST(testDialogPaste
);
264 CPPUNIT_TEST(testShowHideDialog
);
265 CPPUNIT_TEST(testDialogInput
);
266 CPPUNIT_TEST(testCalcSaveAs
);
267 CPPUNIT_TEST(testControlState
);
268 CPPUNIT_TEST(testMetricField
);
269 CPPUNIT_TEST(testMultiDocuments
);
270 CPPUNIT_TEST(testJumpCursor
);
271 CPPUNIT_TEST(testABI
);
272 CPPUNIT_TEST_SUITE_END();
274 uno::Reference
<lang::XComponent
> mxComponent
;
275 OString m_aTextSelection
;
276 OString m_aTextSelectionStart
;
277 OString m_aTextSelectionEnd
;
278 std::vector
<OString
> m_aSearchResultSelection
;
279 std::vector
<int> m_aSearchResultPart
;
280 int m_nSelectionBeforeSearchResult
;
281 int m_nSelectionAfterSearchResult
;
283 // for testCommandResult
284 osl::Condition m_aCommandResultCondition
;
285 OString m_aCommandResult
;
287 // for testModifiedStatus
288 osl::Condition m_aStateChangedCondition
;
292 // for testContextMenu{Calc, Writer}
293 osl::Condition m_aContextMenuCondition
;
294 boost::property_tree::ptree m_aContextMenuResult
;
296 std::unique_ptr
<LibLODocument_Impl
> m_pDocument
;
299 DesktopLOKTest::~DesktopLOKTest()
306 static Control
* GetFocusControl(vcl::Window
const * pParent
)
308 sal_uInt16 nChildren
= pParent
->GetChildCount();
309 for (sal_uInt16 nChild
= 0; nChild
< nChildren
; ++nChild
)
311 vcl::Window
* pChild
= pParent
->GetChild( nChild
);
312 Control
* pCtrl
= dynamic_cast<Control
*>(pChild
);
313 if (pCtrl
&& pCtrl
->HasControlFocus())
316 Control
* pSubCtrl
= GetFocusControl( pChild
);
323 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
324 DesktopLOKTest::loadDocUrlImpl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
329 case LOK_DOCTYPE_TEXT
:
330 aService
= "com.sun.star.text.TextDocument";
332 case LOK_DOCTYPE_SPREADSHEET
:
333 aService
= "com.sun.star.sheet.SpreadsheetDocument";
335 case LOK_DOCTYPE_PRESENTATION
:
336 aService
= "com.sun.star.presentation.PresentationDocument";
339 CPPUNIT_ASSERT(false);
343 static int nDocumentIdCounter
= 0;
344 SfxViewShell::SetCurrentDocId(ViewShellDocId(nDocumentIdCounter
));
345 uno::Reference
<lang::XComponent
> xComponent
= loadFromDesktop(rFileURL
, aService
);
347 std::unique_ptr
<LibLODocument_Impl
> pDocument(new LibLODocument_Impl(xComponent
, nDocumentIdCounter
));
348 ++nDocumentIdCounter
;
350 return std::make_pair(std::move(pDocument
), xComponent
);
353 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
354 DesktopLOKTest::loadDocImpl(const char* pName
, LibreOfficeKitDocumentType eType
)
357 createFileURL(OUString::createFromAscii(pName
), aFileURL
);
358 return loadDocUrlImpl(aFileURL
, eType
);
361 std::pair
<std::unique_ptr
<LibLODocument_Impl
>, uno::Reference
<lang::XComponent
>>
362 DesktopLOKTest::loadDocImpl(const char* pName
)
364 return loadDocImpl(pName
, getDocumentTypeFromName(pName
));
367 LibLODocument_Impl
* DesktopLOKTest::loadDocUrl(const OUString
& rFileURL
, LibreOfficeKitDocumentType eType
)
369 std::tie(m_pDocument
, mxComponent
) = loadDocUrlImpl(rFileURL
, eType
);
370 return m_pDocument
.get();
373 LibLODocument_Impl
* DesktopLOKTest::loadDoc(const char* pName
, LibreOfficeKitDocumentType eType
)
375 std::tie(m_pDocument
, mxComponent
) = loadDocImpl(pName
, eType
);
376 return m_pDocument
.get();
379 void DesktopLOKTest::closeDoc(std::unique_ptr
<LibLODocument_Impl
>& pDocument
,
380 uno::Reference
<lang::XComponent
>& xComponent
)
384 pDocument
->pClass
->registerCallback(pDocument
.get(), nullptr, nullptr);
390 closeDocument(xComponent
);
395 void DesktopLOKTest::callback(int nType
, const char* pPayload
, void* pData
)
397 static_cast<DesktopLOKTest
*>(pData
)->callbackImpl(nType
, pPayload
);
400 void DesktopLOKTest::callbackImpl(int nType
, const char* pPayload
)
404 case LOK_CALLBACK_TEXT_SELECTION
:
406 m_aTextSelection
= pPayload
;
407 if (m_aSearchResultSelection
.empty())
408 ++m_nSelectionBeforeSearchResult
;
410 ++m_nSelectionAfterSearchResult
;
413 case LOK_CALLBACK_TEXT_SELECTION_START
:
414 m_aTextSelectionStart
= pPayload
;
416 case LOK_CALLBACK_TEXT_SELECTION_END
:
417 m_aTextSelectionEnd
= pPayload
;
419 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
421 m_aSearchResultSelection
.clear();
422 boost::property_tree::ptree aTree
;
423 std::stringstream
aStream(pPayload
);
424 boost::property_tree::read_json(aStream
, aTree
);
425 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("searchResultSelection"))
427 m_aSearchResultSelection
.emplace_back(rValue
.second
.get
<std::string
>("rectangles").c_str());
428 m_aSearchResultPart
.push_back(std::atoi(rValue
.second
.get
<std::string
>("part").c_str()));
432 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
434 m_aCommandResult
= pPayload
;
435 m_aCommandResultCondition
.set();
438 case LOK_CALLBACK_STATE_CHANGED
:
440 OString
aPayload(pPayload
);
441 OString
aPrefix(".uno:ModifiedStatus=");
442 if (aPayload
.startsWith(aPrefix
))
444 m_bModified
= aPayload
.copy(aPrefix
.getLength()).toBoolean();
445 m_aStateChangedCondition
.set();
447 else if (aPayload
.startsWith(".uno:TrackChanges=") && aPayload
.endsWith("=true"))
451 case LOK_CALLBACK_CONTEXT_MENU
:
453 m_aContextMenuResult
.clear();
454 std::stringstream
aStream(pPayload
);
455 boost::property_tree::read_json(aStream
, m_aContextMenuResult
);
456 m_aContextMenuCondition
.set();
462 void DesktopLOKTest::testGetStyles()
464 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
465 boost::property_tree::ptree aTree
;
466 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:StyleApply");
467 std::stringstream
aStream(pJSON
);
468 boost::property_tree::read_json(aStream
, aTree
);
469 CPPUNIT_ASSERT( !aTree
.empty() );
470 CPPUNIT_ASSERT_EQUAL( std::string(".uno:StyleApply"), aTree
.get_child("commandName").get_value
<std::string
>() );
472 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
473 CPPUNIT_ASSERT( !aValues
.empty() );
474 for (const auto& rPair
: aValues
)
476 if( rPair
.first
!= "ClearStyle")
478 CPPUNIT_ASSERT( !rPair
.second
.empty());
480 if (rPair
.first
!= "CharacterStyles" &&
481 rPair
.first
!= "ParagraphStyles" &&
482 rPair
.first
!= "FrameStyles" &&
483 rPair
.first
!= "PageStyles" &&
484 rPair
.first
!= "NumberingStyles" &&
485 rPair
.first
!= "CellStyles" &&
486 rPair
.first
!= "ShapeStyles" &&
487 rPair
.first
!= "TableStyles" &&
488 rPair
.first
!= "HeaderFooter" &&
489 rPair
.first
!= "Commands")
491 CPPUNIT_FAIL("Unknown style family: " + rPair
.first
);
497 void DesktopLOKTest::testGetFonts()
499 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
500 boost::property_tree::ptree aTree
;
501 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CharFontName");
502 std::stringstream
aStream(pJSON
);
503 boost::property_tree::read_json(aStream
, aTree
);
504 CPPUNIT_ASSERT( !aTree
.empty() );
505 CPPUNIT_ASSERT_EQUAL( std::string(".uno:CharFontName"), aTree
.get_child("commandName").get_value
<std::string
>() );
507 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
508 CPPUNIT_ASSERT( !aValues
.empty() );
509 for (const auto& rPair
: aValues
)
511 // check that we have font sizes available for each font
512 CPPUNIT_ASSERT( !rPair
.second
.empty());
517 void DesktopLOKTest::testCreateView()
519 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
520 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
522 int nId0
= pDocument
->m_pDocumentClass
->getView(pDocument
);
523 int nId1
= pDocument
->m_pDocumentClass
->createView(pDocument
);
524 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
526 // Test getViewIds().
527 std::vector
<int> aViewIds(2);
528 CPPUNIT_ASSERT(pDocument
->m_pDocumentClass
->getViewIds(pDocument
, aViewIds
.data(), aViewIds
.size()));
529 CPPUNIT_ASSERT_EQUAL(nId0
, aViewIds
[0]);
530 CPPUNIT_ASSERT_EQUAL(nId1
, aViewIds
[1]);
532 // Make sure the created view is the active one, then switch to the old
534 CPPUNIT_ASSERT_EQUAL(nId1
, pDocument
->m_pDocumentClass
->getView(pDocument
));
535 pDocument
->m_pDocumentClass
->setView(pDocument
, nId0
);
536 CPPUNIT_ASSERT_EQUAL(nId0
, pDocument
->m_pDocumentClass
->getView(pDocument
));
538 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nId1
);
539 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
542 void DesktopLOKTest::testGetPartPageRectangles()
544 // Test that we get as many page rectangles as expected: blank document is
546 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
547 char* pRectangles
= pDocument
->pClass
->getPartPageRectangles(pDocument
);
548 OUString sRectangles
= OUString::fromUtf8(pRectangles
);
550 std::vector
<OUString
> aRectangles
;
551 sal_Int32 nIndex
= 0;
554 OUString aRectangle
= sRectangles
.getToken(0, ';', nIndex
);
555 if (!aRectangle
.isEmpty())
556 aRectangles
.push_back(aRectangle
);
559 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aRectangles
.size());
564 void DesktopLOKTest::testGetFilterTypes()
566 LibLibreOffice_Impl aOffice
;
567 char* pJSON
= aOffice
.m_pOfficeClass
->getFilterTypes(&aOffice
);
569 std::stringstream
aStream(pJSON
);
570 boost::property_tree::ptree aTree
;
571 boost::property_tree::read_json(aStream
, aTree
);
573 CPPUNIT_ASSERT(!aTree
.empty());
574 CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.oasis.opendocument.text"), aTree
.get_child("writer8").get_child("MediaType").get_value
<std::string
>());
578 void DesktopLOKTest::testSearchCalc()
580 LibLibreOffice_Impl aOffice
;
581 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
582 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
583 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
585 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
587 {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
588 {"SearchItem.Backward", uno::makeAny(false)},
589 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
591 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues
);
592 Scheduler::ProcessEventsToIdle();
594 std::vector
<OString
> aSelections
;
595 sal_Int32 nIndex
= 0;
598 OString aToken
= m_aTextSelection
.getToken(0, ';', nIndex
);
599 aSelections
.push_back(aToken
);
600 } while (nIndex
>= 0);
601 // This was 1, find-all only found one match.
602 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aSelections
.size());
603 // Make sure that we get exactly as many rectangle lists as matches.
604 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), m_aSearchResultSelection
.size());
605 // Result is on the first sheet.
606 CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart
[0]);
609 void DesktopLOKTest::testSearchAllNotificationsCalc()
611 LibLibreOffice_Impl aOffice
;
612 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
613 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
614 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
616 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
618 {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
619 {"SearchItem.Backward", uno::makeAny(false)},
620 {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16
>(SvxSearchCmd::FIND_ALL
))},
622 comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues
);
623 Scheduler::ProcessEventsToIdle();
625 // This was 1, make sure that we get no notifications about selection changes during search.
626 CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult
);
627 // But we do get the selection afterwards.
628 CPPUNIT_ASSERT(m_nSelectionAfterSearchResult
> 0);
631 void DesktopLOKTest::testPaintTile()
633 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
634 int nCanvasWidth
= 100;
635 int nCanvasHeight
= 300;
636 sal_Int32 nStride
= cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32
, nCanvasWidth
);
637 std::vector
<unsigned char> aBuffer(nStride
* nCanvasHeight
);
640 int nTileWidth
= 1000;
641 int nTileHeight
= 3000;
643 // This used to crash: paintTile() implementation did not handle
644 // nCanvasWidth != nCanvasHeight correctly, as usually both are just always
646 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
648 // This crashed in OutputDevice::DrawDeviceAlphaBitmap().
653 aBuffer
.resize(nCanvasWidth
* nCanvasHeight
* 4);
654 pDocument
->pClass
->paintTile(pDocument
, aBuffer
.data(), nCanvasWidth
, nCanvasHeight
, nTilePosX
, nTilePosY
, nTileWidth
, nTileHeight
);
657 void DesktopLOKTest::testSaveAs()
659 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
660 utl::TempFile aTempFile
;
661 aTempFile
.EnableKillingFile();
662 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
665 void DesktopLOKTest::testSaveAsCalc()
667 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
668 utl::TempFile aTempFile
;
669 aTempFile
.EnableKillingFile();
670 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "png", nullptr));
673 void DesktopLOKTest::testPasteWriter()
675 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
676 OString
aText("hello");
678 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
680 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
681 Scheduler::ProcessEventsToIdle();
682 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
683 CPPUNIT_ASSERT_EQUAL(OString("hello"), OString(pText
));
686 // textt/plain should be rejected.
687 CPPUNIT_ASSERT(!pDocument
->pClass
->paste(pDocument
, "textt/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
688 // Writer is expected to support text/html.
689 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aText
.getStr(), aText
.getLength()));
691 // Overwrite doc contents with a HTML paste.
692 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
693 Scheduler::ProcessEventsToIdle();
694 OString
aComment("foo <!-- bar --> baz");
695 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/html", aComment
.getStr(), aComment
.getLength()));
697 // Check if we have a comment.
698 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
699 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
700 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
701 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
702 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
703 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
704 CPPUNIT_ASSERT_EQUAL(OUString("Text"), xTextPortion
->getPropertyValue("TextPortionType").get
<OUString
>());
705 // Without the accompanying fix in place, this test would have failed, as we had a comment
706 // between "foo" and "baz".
707 CPPUNIT_ASSERT(!xTextPortionEnumeration
->hasMoreElements());
710 void DesktopLOKTest::testPasteWriterJPEG()
712 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
715 createFileURL(u
"paste.jpg", aFileURL
);
716 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
717 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
719 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
721 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
722 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
723 // This was 0, JPEG was not handled as a format for clipboard paste.
724 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(1), xDrawPage
->getCount());
726 uno::Reference
<beans::XPropertySet
> xShape(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
727 // This was text::TextContentAnchorType_AT_PARAGRAPH.
728 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER
, xShape
->getPropertyValue("AnchorType").get
<text::TextContentAnchorType
>());
730 // Delete the pasted picture, and paste again with a custom anchor type.
731 uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY_THROW
)->dispose();
732 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
734 {"AnchorType", uno::makeAny(static_cast<sal_uInt16
>(text::TextContentAnchorType_AT_CHARACTER
))},
736 comphelper::dispatchCommand(".uno:Paste", aPropertyValues
);
737 xShape
.set(xDrawPage
->getByIndex(0), uno::UNO_QUERY
);
738 // This was text::TextContentAnchorType_AS_CHARACTER, AnchorType argument was ignored.
739 CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AT_CHARACTER
, xShape
->getPropertyValue("AnchorType").get
<text::TextContentAnchorType
>());
742 void DesktopLOKTest::testUndoWriter()
744 // Load a Writer document and press a key.
745 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
746 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
747 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
748 Scheduler::ProcessEventsToIdle();
750 boost::property_tree::ptree aTree
;
751 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:Undo");
752 std::stringstream
aStream(pJSON
);
754 CPPUNIT_ASSERT(!aStream
.str().empty());
755 boost::property_tree::read_json(aStream
, aTree
);
756 // Make sure that pressing a key creates exactly one undo action.
757 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("actions").size());
760 void DesktopLOKTest::testRowColumnHeaders()
768 * "size": "254.987250637468",
772 * "size": "509.974501274936",
778 * "size": "1274.93625318734",
782 * "size": "2549.87250637468",
788 * "size" defines the bottom/right boundary of a row/column in twips (size between 0 and boundary)
789 * "text" has the header label in UTF-8
791 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
793 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
795 tools::Long nWidth
= 0;
796 tools::Long nHeight
= 0;
797 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
798 tools::Long nX
= rtl::math::round(nWidth
/ 4.0);
799 tools::Long nY
= rtl::math::round(nHeight
/ 4.0);
800 nWidth
= rtl::math::round(nWidth
/ 2.0);
801 nHeight
= rtl::math::round(nHeight
/ 2.0);
803 std::stringstream aPayload
;
804 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
806 boost::property_tree::ptree aTree
;
807 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
808 std::stringstream
aStream(pJSON
);
811 CPPUNIT_ASSERT(!aStream
.str().empty());
813 boost::property_tree::read_json(aStream
, aTree
);
814 sal_Int32 nPrevious
= 0;
815 bool bFirstHeader
= true;
816 bool bNotEnoughHeaders
= true;
817 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
819 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
820 nSize
*= 15; /* TWIPS_PER_PIXEL */
821 OString
aText(rValue
.second
.get
<std::string
>("text").c_str());
825 CPPUNIT_ASSERT(nSize
<= nY
);
826 CPPUNIT_ASSERT_EQUAL(OString("10"), aText
);
827 bFirstHeader
= false;
831 CPPUNIT_ASSERT(nSize
> 0);
832 CPPUNIT_ASSERT(nPrevious
< nSize
);
833 if (nSize
> nY
+ nHeight
)
835 bNotEnoughHeaders
= false;
841 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
845 bNotEnoughHeaders
= true;
846 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("columns"))
848 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
849 nSize
*= 15; /* TWIPS_PER_PIXEL */
850 OString
aText(rValue
.second
.get
<std::string
>("text").c_str());
853 CPPUNIT_ASSERT(nSize
<= nX
);
854 CPPUNIT_ASSERT_EQUAL(OString("3"), aText
);
855 bFirstHeader
= false;
859 CPPUNIT_ASSERT(nSize
> 0);
860 CPPUNIT_ASSERT(nPrevious
< nSize
);
861 if (nSize
> nX
+ nWidth
)
863 bNotEnoughHeaders
= false;
869 CPPUNIT_ASSERT(!bNotEnoughHeaders
);
872 void DesktopLOKTest::testHiddenRowHeaders()
874 LibLODocument_Impl
* pDocument
= loadDoc("hidden-row.ods");
876 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
878 tools::Long
const nX
= 0;
879 tools::Long
const nY
= 0;
880 tools::Long nWidth
= 0;
881 tools::Long nHeight
= 0;
882 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
884 std::stringstream aPayload
;
885 aPayload
<< ".uno:ViewRowColumnHeaders?x=" << nX
<< "&y=" << nY
<< "&width=" << nWidth
<< "&height=" << nHeight
;
887 boost::property_tree::ptree aTree
;
888 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aPayload
.str().c_str());
889 std::stringstream
aStream(pJSON
);
890 CPPUNIT_ASSERT(!aStream
.str().empty());
892 boost::property_tree::read_json(aStream
, aTree
);
894 sal_Int32 nPrevious
= 0;
895 sal_Int32 nIndex
= 0;
896 for (const boost::property_tree::ptree::value_type
& rValue
: aTree
.get_child("rows"))
898 sal_Int32 nSize
= OString(rValue
.second
.get
<std::string
>("size").c_str()).toInt32();
902 // nSize was 510, nPrevious was 255, i.e. hidden row wasn't reported as 0 height.
903 CPPUNIT_ASSERT_EQUAL(nPrevious
, nSize
);
910 void DesktopLOKTest::testCellCursor()
912 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
914 boost::property_tree::ptree aTree
;
916 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:CellCursor?tileWidth=1&tileHeight=1&outputWidth=1&outputHeight=1");
918 std::stringstream
aStream(pJSON
);
920 CPPUNIT_ASSERT(!aStream
.str().empty());
922 boost::property_tree::read_json(aStream
, aTree
);
924 OString
aRectangle(aTree
.get
<std::string
>("commandValues").c_str());
925 // cell cursor geometry + col + row
926 CPPUNIT_ASSERT_EQUAL(OString("0, 0, 1274, 254, 0, 0"), aRectangle
);
929 void DesktopLOKTest::testCommandResult()
931 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
933 // the postUnoCommand() is supposed to be async, let's test it safely
934 // [no idea if it is async in reality - most probably we are operating
935 // under some solar mutex or something anyway ;-) - but...]
936 TimeValue aTimeValue
= { 2 , 0 }; // 2 seconds max
938 // nothing is triggered when we have no callback yet, we just time out on
939 // the condition var.
940 m_aCommandResultCondition
.reset();
941 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
942 Scheduler::ProcessEventsToIdle();
943 m_aCommandResultCondition
.wait(aTimeValue
);
945 CPPUNIT_ASSERT(m_aCommandResult
.isEmpty());
947 // but we get some real values when the callback is set up
948 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
950 m_aCommandResultCondition
.reset();
951 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Bold", nullptr, true);
952 Scheduler::ProcessEventsToIdle();
953 m_aCommandResultCondition
.wait(aTimeValue
);
955 boost::property_tree::ptree aTree
;
956 std::stringstream
aStream(m_aCommandResult
.getStr());
957 boost::property_tree::read_json(aStream
, aTree
);
959 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), aTree
.get_child("commandName").get_value
<std::string
>());
960 CPPUNIT_ASSERT_EQUAL(true, aTree
.get_child("success").get_value
<bool>());
963 void DesktopLOKTest::testWriterComments()
965 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
966 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
967 uno::Reference
<awt::XReschedule
> xToolkit
= com::sun::star::awt::Toolkit::create(comphelper::getProcessComponentContext());
969 // Insert a comment at the beginning of the document and wait till the main
970 // loop grabs the focus, so characters end up in the annotation window.
971 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
972 m_aCommandResultCondition
.reset();
973 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", nullptr, true);
974 Scheduler::ProcessEventsToIdle();
975 m_aCommandResultCondition
.wait(aTimeValue
);
976 CPPUNIT_ASSERT(!m_aCommandResult
.isEmpty());
977 xToolkit
->reschedule();
979 // Test that we have a comment.
980 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
981 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xTextDocument
->getText(), uno::UNO_QUERY
);
982 uno::Reference
<container::XEnumeration
> xParagraphEnumeration
= xParagraphEnumerationAccess
->createEnumeration();
983 uno::Reference
<container::XEnumerationAccess
> xParagraph(xParagraphEnumeration
->nextElement(), uno::UNO_QUERY
);
984 uno::Reference
<container::XEnumeration
> xTextPortionEnumeration
= xParagraph
->createEnumeration();
985 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortionEnumeration
->nextElement(), uno::UNO_QUERY
);
986 CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), xTextPortion
->getPropertyValue("TextPortionType").get
<OUString
>());
988 // Type "test" and finish editing.
989 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
990 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'e', 0);
991 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 's', 0);
992 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
993 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
994 Scheduler::ProcessEventsToIdle();
996 // Test that the typed characters ended up in the right window.
997 auto xTextField
= xTextPortion
->getPropertyValue("TextField").get
< uno::Reference
<beans::XPropertySet
> >();
998 // This was empty, typed characters ended up in the body text.
999 CPPUNIT_ASSERT_EQUAL(OUString("test"), xTextField
->getPropertyValue("Content").get
<OUString
>());
1002 void DesktopLOKTest::testTrackChanges()
1004 // Load a document and create two views.
1005 LibLibreOffice_Impl aOffice
;
1006 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1007 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1008 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1009 pDocument
->pClass
->createView(pDocument
);
1010 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1011 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1012 Scheduler::ProcessEventsToIdle();
1014 // Enable track changes and assert that both views get notified.
1015 m_nTrackChanges
= 0;
1016 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:TrackChanges", nullptr, false);
1017 Scheduler::ProcessEventsToIdle();
1018 // This was 1, only the active view was notified.
1019 CPPUNIT_ASSERT_EQUAL(2, m_nTrackChanges
);
1022 void DesktopLOKTest::testSheetOperations()
1024 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
1026 // insert the last sheet
1027 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
1028 "{ \"Name\": { \"type\": \"string\", \"value\": \"LastSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 0 } }", false);
1030 // insert the first sheet
1031 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Insert",
1032 "{ \"Name\": { \"type\": \"string\", \"value\": \"FirstSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 1 } }", false);
1034 // rename the \"Sheet1\" (2nd now) to \"Renamed\"
1035 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Name",
1036 "{ \"Name\": { \"type\": \"string\", \"value\": \"Renamed\" }, \"Index\": { \"type\": \"long\", \"value\": 2 } }", false);
1038 // delete the \"Sheet2\" (3rd)
1039 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:Remove",
1040 "{ \"Index\": { \"type\": \"long\", \"value\": 3 } }", false);
1042 Scheduler::ProcessEventsToIdle();
1043 CPPUNIT_ASSERT_EQUAL(6, pDocument
->pClass
->getParts(pDocument
));
1045 std::vector
<OString
> aExpected
= { "FirstSheet", "Renamed", "Sheet3", "Sheet4", "Sheet5", "LastSheet" };
1046 for (int i
= 0; i
< 6; ++i
)
1048 char* pPartName
= pDocument
->pClass
->getPartName(pDocument
, i
);
1049 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], OString(pPartName
));
1054 void DesktopLOKTest::testSheetSelections()
1056 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods", LOK_DOCTYPE_SPREADSHEET
);
1057 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1058 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1061 * Check if selection data is correct
1066 int const col2
= 2200;
1067 int const col3
= 3300;
1071 // Select row 5 from column 1 through column 5
1072 pDocument
->pClass
->postMouseEvent(pDocument
,
1073 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1076 pDocument
->pClass
->postMouseEvent(pDocument
,
1077 LOK_MOUSEEVENT_MOUSEMOVE
,
1080 pDocument
->pClass
->postMouseEvent(pDocument
,
1081 LOK_MOUSEEVENT_MOUSEMOVE
,
1084 pDocument
->pClass
->postMouseEvent(pDocument
,
1085 LOK_MOUSEEVENT_MOUSEMOVE
,
1088 pDocument
->pClass
->postMouseEvent(pDocument
,
1089 LOK_MOUSEEVENT_MOUSEMOVE
,
1092 pDocument
->pClass
->postMouseEvent(pDocument
,
1093 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1096 Scheduler::ProcessEventsToIdle();
1098 // Copy the contents and check if matches expected data
1100 char* pUsedMimeType
= nullptr;
1101 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1102 std::vector
<long> aExpected
= {5, 6, 7, 8, 9};
1103 std::istringstream
iss(pCopiedContent
);
1104 for (size_t i
= 0; i
< aExpected
.size(); i
++)
1108 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], strtol(token
.c_str(), nullptr, 10));
1111 free(pUsedMimeType
);
1112 free(pCopiedContent
);
1116 * Check if clicking inside the selection deselects the whole selection
1118 int const row10
= 2400;
1119 // Select starting from row5, col1 to row10, col5
1120 pDocument
->pClass
->postMouseEvent(pDocument
,
1121 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1124 pDocument
->pClass
->postMouseEvent(pDocument
,
1125 LOK_MOUSEEVENT_MOUSEMOVE
,
1128 pDocument
->pClass
->postMouseEvent(pDocument
,
1129 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1133 // Click at row5, col4
1134 pDocument
->pClass
->postMouseEvent(pDocument
,
1135 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1138 pDocument
->pClass
->postMouseEvent(pDocument
,
1139 LOK_MOUSEEVENT_MOUSEBUTTONUP
,
1142 Scheduler::ProcessEventsToIdle();
1144 // Selected text should get deselected and copying should give us
1145 // content of only one cell, now
1147 char* pUsedMimeType
= nullptr;
1148 char* pCopiedContent
= pDocument
->pClass
->getTextSelection(pDocument
, nullptr, &pUsedMimeType
);
1149 std::vector
<long> aExpected
= { 8 };
1150 std::istringstream
iss(pCopiedContent
);
1151 for (size_t i
= 0; i
< aExpected
.size(); i
++)
1155 CPPUNIT_ASSERT_EQUAL(aExpected
[i
], strtol(token
.c_str(), nullptr, 10));
1158 free(pUsedMimeType
);
1159 free(pCopiedContent
);
1165 void verifyContextMenuStructure(boost::property_tree::ptree
& aRoot
)
1167 for (const auto& aItemPair
: aRoot
)
1169 // This is an array, so no key
1170 CPPUNIT_ASSERT_EQUAL(aItemPair
.first
, std::string(""));
1172 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1173 boost::optional
<boost::property_tree::ptree
&> aText
= aItemValue
.get_child_optional("text");
1174 boost::optional
<boost::property_tree::ptree
&> aType
= aItemValue
.get_child_optional("type");
1175 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1176 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aItemValue
.get_child_optional("menu");
1177 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aItemValue
.get_child_optional("enabled");
1178 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aItemValue
.get_child_optional("checktype");
1179 boost::optional
<boost::property_tree::ptree
&> aChecked
= aItemValue
.get_child_optional("checked");
1181 // type is omnipresent
1182 CPPUNIT_ASSERT( aType
);
1184 // separator doesn't have any other attribs
1185 if ( aType
.get().data() == "separator" )
1187 CPPUNIT_ASSERT( !aText
);
1188 CPPUNIT_ASSERT( !aCommand
);
1189 CPPUNIT_ASSERT( !aSubmenu
);
1190 CPPUNIT_ASSERT( !aEnabled
);
1191 CPPUNIT_ASSERT( !aChecktype
);
1192 CPPUNIT_ASSERT( !aChecked
);
1194 else if ( aType
.get().data() == "command" )
1196 CPPUNIT_ASSERT( aCommand
);
1197 CPPUNIT_ASSERT( aText
);
1199 else if ( aType
.get().data() == "menu")
1201 CPPUNIT_ASSERT( aSubmenu
);
1202 CPPUNIT_ASSERT( aText
);
1203 verifyContextMenuStructure( aSubmenu
.get() );
1208 CPPUNIT_ASSERT( aChecktype
.get().data() == "radio" ||
1209 aChecktype
.get().data() == "checkmark" ||
1210 aChecktype
.get().data() == "auto" );
1212 CPPUNIT_ASSERT( aChecked
);
1213 CPPUNIT_ASSERT( aChecked
.get().data() == "true" || aChecked
.get().data() == "false" );
1219 boost::optional
<boost::property_tree::ptree
>
1220 getContextMenuItem(boost::property_tree::ptree
& aMenu
, std::string
const & unoSelector
)
1222 boost::optional
<boost::property_tree::ptree
> aMenuItem
;
1223 for (const auto& aItemPair
: aMenu
)
1225 boost::property_tree::ptree aItemValue
= aItemPair
.second
;
1227 boost::optional
<boost::property_tree::ptree
&> aCommand
= aItemValue
.get_child_optional("command");
1228 if (aCommand
&& aCommand
.get().data() == unoSelector
)
1230 aMenuItem
= aItemValue
;
1238 } // end anonymous namespace
1240 void DesktopLOKTest::testContextMenuCalc()
1242 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
1243 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1244 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1247 Point
aPointOnImage(1150, 1100);
1248 pDocument
->pClass
->postMouseEvent(pDocument
,
1249 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1250 aPointOnImage
.X(), aPointOnImage
.Y(),
1252 Scheduler::ProcessEventsToIdle();
1254 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1255 m_aContextMenuCondition
.wait(aTimeValue
);
1257 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1258 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1259 CPPUNIT_ASSERT( aMenu
);
1260 verifyContextMenuStructure( aMenu
.get() );
1262 // tests for calc specific context menu
1265 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1266 CPPUNIT_ASSERT(aMenuItem
);
1268 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1269 CPPUNIT_ASSERT(aEnabled
);
1270 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1275 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1276 CPPUNIT_ASSERT(aMenuItem
);
1278 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1279 CPPUNIT_ASSERT(aEnabled
);
1280 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1285 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1286 CPPUNIT_ASSERT(aMenuItem
);
1288 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1289 CPPUNIT_ASSERT(aEnabled
);
1290 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1293 // Remove hyperlink is disabled
1295 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:DeleteShapeHyperlink");
1296 CPPUNIT_ASSERT(aMenuItem
);
1298 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1299 CPPUNIT_ASSERT(aEnabled
);
1300 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1303 // open hyperlink is disabled
1305 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:OpenHyperlinkOnCursor");
1306 CPPUNIT_ASSERT(aMenuItem
);
1308 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1309 CPPUNIT_ASSERT(aEnabled
);
1310 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1313 // checkbutton tests
1315 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:AnchorMenu");
1316 CPPUNIT_ASSERT(aMenuItem
);
1318 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1319 CPPUNIT_ASSERT(aSubmenu
);
1321 boost::optional
<boost::property_tree::ptree
> aMenuItemToPage
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToPage");
1322 CPPUNIT_ASSERT(aMenuItemToPage
);
1324 boost::optional
<boost::property_tree::ptree
> aMenuItemToCell
= getContextMenuItem(aSubmenu
.get(), ".uno:SetAnchorToCell");
1325 CPPUNIT_ASSERT(aMenuItemToCell
);
1327 // these are radio buttons
1328 boost::optional
<boost::property_tree::ptree
&> aChecktypeToPage
= aMenuItemToPage
.get().get_child_optional("checktype");
1329 CPPUNIT_ASSERT(aChecktypeToPage
);
1330 CPPUNIT_ASSERT_EQUAL(aChecktypeToPage
.get().data(), std::string("radio"));
1332 boost::optional
<boost::property_tree::ptree
&> aChecktypeToCell
= aMenuItemToCell
.get().get_child_optional("checktype");
1333 CPPUNIT_ASSERT(aChecktypeToCell
);
1334 CPPUNIT_ASSERT_EQUAL(aChecktypeToCell
.get().data(), std::string("radio"));
1336 // ToPage is checked
1337 boost::optional
<boost::property_tree::ptree
&> aCheckedToPage
= aMenuItemToPage
.get().get_child_optional("checked");
1338 CPPUNIT_ASSERT(aCheckedToPage
);
1339 CPPUNIT_ASSERT_EQUAL(aCheckedToPage
.get().data(), std::string("true"));
1341 // ToCell is unchecked
1342 boost::optional
<boost::property_tree::ptree
&> aCheckedToCell
= aMenuItemToCell
.get().get_child_optional("checked");
1343 CPPUNIT_ASSERT(aCheckedToCell
);
1344 CPPUNIT_ASSERT_EQUAL(aCheckedToCell
.get().data(), std::string("false"));
1348 void DesktopLOKTest::testContextMenuWriter()
1350 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1351 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1352 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1354 Point
aRandomPoint(1150, 1100);
1355 pDocument
->pClass
->postMouseEvent(pDocument
,
1356 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1357 aRandomPoint
.X(), aRandomPoint
.Y(),
1359 Scheduler::ProcessEventsToIdle();
1361 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1362 m_aContextMenuCondition
.wait(aTimeValue
);
1364 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1365 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1366 CPPUNIT_ASSERT( aMenu
);
1367 verifyContextMenuStructure( aMenu
.get() );
1369 // tests for writer specific context menu
1372 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1373 CPPUNIT_ASSERT(aMenuItem
);
1375 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1376 CPPUNIT_ASSERT(aEnabled
);
1377 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1382 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1383 CPPUNIT_ASSERT(aMenuItem
);
1385 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1386 CPPUNIT_ASSERT(aEnabled
);
1387 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1392 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1393 CPPUNIT_ASSERT(aMenuItem
);
1395 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1396 CPPUNIT_ASSERT(aEnabled
);
1397 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1401 void DesktopLOKTest::testContextMenuImpress()
1403 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp", LOK_DOCTYPE_PRESENTATION
);
1404 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
1405 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
1407 // random point where we don't hit an underlying comment or text box
1408 Point
aRandomPoint(10, 1150);
1409 pDocument
->pClass
->postMouseEvent(pDocument
,
1410 LOK_MOUSEEVENT_MOUSEBUTTONDOWN
,
1411 aRandomPoint
.X(), aRandomPoint
.Y(),
1413 Scheduler::ProcessEventsToIdle();
1415 TimeValue
const aTimeValue
= {2 , 0}; // 2 seconds max
1416 m_aContextMenuCondition
.wait(aTimeValue
);
1418 CPPUNIT_ASSERT( !m_aContextMenuResult
.empty() );
1419 boost::optional
<boost::property_tree::ptree
&> aMenu
= m_aContextMenuResult
.get_child_optional("menu");
1420 CPPUNIT_ASSERT( aMenu
);
1421 verifyContextMenuStructure( aMenu
.get() );
1423 // tests for impress specific context menu
1426 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Cut");
1427 CPPUNIT_ASSERT(aMenuItem
);
1429 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1430 CPPUNIT_ASSERT(aEnabled
);
1431 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1436 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Copy");
1437 CPPUNIT_ASSERT(aMenuItem
);
1439 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1440 CPPUNIT_ASSERT(aEnabled
);
1441 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1446 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:Paste");
1447 CPPUNIT_ASSERT(aMenuItem
);
1449 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1450 CPPUNIT_ASSERT(aEnabled
);
1451 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("true"));
1454 // SaveBackground is disabled
1456 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SaveBackground");
1457 CPPUNIT_ASSERT(aMenuItem
);
1459 boost::optional
<boost::property_tree::ptree
&> aEnabled
= aMenuItem
.get().get_child_optional("enabled");
1460 CPPUNIT_ASSERT(aEnabled
);
1461 CPPUNIT_ASSERT_EQUAL(aEnabled
.get().data(), std::string("false"));
1464 // checkbutton tests
1466 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:ShowRuler");
1467 CPPUNIT_ASSERT(aMenuItem
);
1469 boost::optional
<boost::property_tree::ptree
&> aChecktype
= aMenuItem
.get().get_child_optional("checktype");
1470 CPPUNIT_ASSERT(aChecktype
);
1471 CPPUNIT_ASSERT_EQUAL(aChecktype
.get().data(), std::string("checkmark"));
1473 boost::optional
<boost::property_tree::ptree
&> aChecked
= aMenuItem
.get().get_child_optional("checked");
1474 CPPUNIT_ASSERT(aChecked
);
1475 CPPUNIT_ASSERT_EQUAL(aChecked
.get().data(), std::string("false"));
1478 // Checkbutton tests inside SnapLines submenu
1480 boost::optional
<boost::property_tree::ptree
> aMenuItem
= getContextMenuItem(aMenu
.get(), ".uno:SnapLinesMenu");
1481 CPPUNIT_ASSERT(aMenuItem
);
1483 boost::optional
<boost::property_tree::ptree
&> aSubmenu
= aMenuItem
.get().get_child_optional("menu");
1484 CPPUNIT_ASSERT(aSubmenu
);
1486 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpVis
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesVisible");
1487 CPPUNIT_ASSERT(aMenuItemHelpVis
);
1489 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpUse
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesUse");
1490 CPPUNIT_ASSERT(aMenuItemHelpUse
);
1492 boost::optional
<boost::property_tree::ptree
> aMenuItemHelpFront
= getContextMenuItem(aSubmenu
.get(), ".uno:HelplinesFront");
1493 CPPUNIT_ASSERT(aMenuItemHelpFront
);
1495 // these are checkmarks
1496 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checktype");
1497 CPPUNIT_ASSERT(aChecktypeHelpVis
);
1498 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpVis
.get().data(), std::string("checkmark"));
1500 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checktype");
1501 CPPUNIT_ASSERT(aChecktypeHelpUse
);
1502 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpUse
.get().data(), std::string("checkmark"));
1504 boost::optional
<boost::property_tree::ptree
&> aChecktypeHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checktype");
1505 CPPUNIT_ASSERT(aChecktypeHelpFront
);
1506 CPPUNIT_ASSERT_EQUAL(aChecktypeHelpFront
.get().data(), std::string("checkmark"));
1508 // HelplineVisible is unchecked
1509 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpVis
= aMenuItemHelpVis
.get().get_child_optional("checked");
1510 CPPUNIT_ASSERT(aCheckedHelpVis
);
1511 CPPUNIT_ASSERT_EQUAL(aCheckedHelpVis
.get().data(), std::string("false"));
1513 // HelplineUse is checked
1514 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpUse
= aMenuItemHelpUse
.get().get_child_optional("checked");
1515 CPPUNIT_ASSERT(aCheckedHelpUse
);
1516 CPPUNIT_ASSERT_EQUAL(aCheckedHelpUse
.get().data(), std::string("true"));
1518 // HelplineFront is checked
1519 boost::optional
<boost::property_tree::ptree
&> aCheckedHelpFront
= aMenuItemHelpFront
.get().get_child_optional("checked");
1520 CPPUNIT_ASSERT(aCheckedHelpFront
);
1521 CPPUNIT_ASSERT_EQUAL(aCheckedHelpFront
.get().data(), std::string("true"));
1525 static void callbackCompressionTest(const int type
, const char* payload
, void* data
)
1527 std::vector
<std::tuple
<int, std::string
>>* notifs
= static_cast<std::vector
<std::tuple
<int, std::string
>>*>(data
);
1528 notifs
->emplace_back(type
, std::string(payload
? payload
: "(nil)"));
1531 void DesktopLOKTest::testNotificationCompression()
1533 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1534 std::vector
<std::tuple
<int, std::string
>> notifs
;
1535 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1537 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""); // 0
1538 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Superseded.
1539 handler
->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
, ""); // Should be dropped.
1540 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // 1
1541 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Should be dropped.
1542 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""); // Superseded.
1543 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""); // 2
1544 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:Bold"); // 3
1545 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ""); // 4
1546 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"); // 5
1547 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // Should be dropped.
1548 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "15, 25, 15, 10"); // Should be dropped.
1549 handler
->queue(LOK_CALLBACK_MOUSE_POINTER
, "text"); // Should be dropped.
1550 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // Superseded.
1551 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // Superseded.
1552 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, "15, 25, 15, 10"); // Superseded.
1553 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // Should be dropped.
1554 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // Should be dropped.
1555 handler
->queue(LOK_CALLBACK_TEXT_SELECTION
, ""); // 7
1556 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_START
, "15, 25, 15, 10"); // 8
1557 handler
->queue(LOK_CALLBACK_TEXT_SELECTION_END
, "15, 25, 15, 10"); // 9
1558 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"); // 10
1559 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""); // 11
1560 handler
->queue(LOK_CALLBACK_CELL_CURSOR
, "15, 25, 15, 10"); // Should be dropped.
1561 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"); // 12
1562 handler
->queue(LOK_CALLBACK_SET_PART
, "1"); // 13
1563 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=20"); // Superseded
1564 handler
->queue(LOK_CALLBACK_CURSOR_VISIBLE
, ""); // Should be dropped.
1565 handler
->queue(LOK_CALLBACK_CELL_FORMULA
, "blah"); // Should be dropped.
1566 handler
->queue(LOK_CALLBACK_SET_PART
, "1"); // Should be dropped.
1567 handler
->queue(LOK_CALLBACK_STATE_CHANGED
, ".uno:AssignLayout=1"); // 14
1569 Scheduler::ProcessEventsToIdle();
1571 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(14), notifs
.size());
1574 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
), std::get
<0>(notifs
[i
]));
1575 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1577 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1578 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1580 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1581 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1583 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1584 CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), std::get
<1>(notifs
[i
++]));
1586 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1587 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1589 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_MOUSE_POINTER
), std::get
<0>(notifs
[i
]));
1590 CPPUNIT_ASSERT_EQUAL(std::string("text"), std::get
<1>(notifs
[i
++]));
1592 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION
), std::get
<0>(notifs
[i
]));
1593 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1595 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_START
), std::get
<0>(notifs
[i
]));
1596 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1598 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_END
), std::get
<0>(notifs
[i
]));
1599 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1601 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_CURSOR
), std::get
<0>(notifs
[i
]));
1602 CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get
<1>(notifs
[i
++]));
1604 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CURSOR_VISIBLE
), std::get
<0>(notifs
[i
]));
1605 CPPUNIT_ASSERT_EQUAL(std::string(""), std::get
<1>(notifs
[i
++]));
1607 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_FORMULA
), std::get
<0>(notifs
[i
]));
1608 CPPUNIT_ASSERT_EQUAL(std::string("blah"), std::get
<1>(notifs
[i
++]));
1610 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_SET_PART
), std::get
<0>(notifs
[i
]));
1611 CPPUNIT_ASSERT_EQUAL(std::string("1"), std::get
<1>(notifs
[i
++]));
1613 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED
), std::get
<0>(notifs
[i
]));
1614 CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get
<1>(notifs
[i
++]));
1617 void DesktopLOKTest::testTileInvalidationCompression()
1619 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1621 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1622 comphelper::ScopeGuard
aGuard([]()
1624 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1627 // Single part merging
1629 std::vector
<std::tuple
<int, std::string
>> notifs
;
1630 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1632 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1633 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1634 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -50, 500, 650, 0");
1635 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0");
1636 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "100, 100, 200, 200, 0");
1638 Scheduler::ProcessEventsToIdle();
1640 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1643 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1644 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 400, 600, 0"), std::get
<1>(notifs
[i
++]));
1649 std::vector
<std::tuple
<int, std::string
>> notifs
;
1650 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1652 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1653 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1"); // Different part
1654 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, 2"); // Invalid
1655 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, 0"); // Inside first
1656 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 1"); // Invalid
1658 Scheduler::ProcessEventsToIdle();
1660 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1663 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1664 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 1"), std::get
<1>(notifs
[i
++]));
1666 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1667 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 239, 239, 0"), std::get
<1>(notifs
[i
++]));
1672 std::vector
<std::tuple
<int, std::string
>> notifs
;
1673 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1675 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0"); // 0
1676 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 1"); // 1: Different part
1677 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1"); // Invalid
1678 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 200, 200, -1"); // 0: All parts
1679 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1"); // Invalid
1680 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-100, -100, 1200, 1200, -1"); // 0: All parts
1681 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 3"); // Overlapped
1682 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 2"); // 1: Unique region
1684 Scheduler::ProcessEventsToIdle();
1686 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1689 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1690 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1100, 1100, -1"), std::get
<1>(notifs
[i
++]));
1692 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1693 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 2"), std::get
<1>(notifs
[i
++]));
1696 // All Parts (partial)
1698 std::vector
<std::tuple
<int, std::string
>> notifs
;
1699 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1701 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 0"); // 0
1702 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 100, 100, 1"); // 1: Different part
1703 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 0, 0, -1"); // Invalid
1704 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 50, 50, -1"); // 2: All-parts
1705 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, -1"); // Invalid
1706 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "150, 150, 40, 40, 3"); // Overlapped w/ 2
1707 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 200, 200, 4"); // 3: Unique
1708 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "1000, 1000, 1239, 1239, 1"); // 4: Unique
1710 Scheduler::ProcessEventsToIdle();
1712 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), notifs
.size());
1715 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1716 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 0"), std::get
<1>(notifs
[i
++]));
1718 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1719 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 100, 100, 1"), std::get
<1>(notifs
[i
++]));
1721 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1722 CPPUNIT_ASSERT_EQUAL(std::string("150, 150, 50, 50, -1"), std::get
<1>(notifs
[i
++]));
1724 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1725 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 4"), std::get
<1>(notifs
[i
++]));
1727 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1728 CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 1"), std::get
<1>(notifs
[i
++]));
1731 // Merge with "EMPTY"
1733 std::vector
<std::tuple
<int, std::string
>> notifs
;
1734 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1736 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 239, 0");
1737 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "EMPTY, 0");
1738 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, 239, 240, 0");
1739 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "-121, -121, 300, 300, 0");
1740 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "0, 0, -32767, -32767, 0");
1742 Scheduler::ProcessEventsToIdle();
1744 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1747 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[i
]));
1748 CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1000000000, 1000000000, 0"), std::get
<1>(notifs
[i
++]));
1752 void DesktopLOKTest::testPartInInvalidation()
1754 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1755 // No part in invalidation: merge.
1757 std::vector
<std::tuple
<int, std::string
>> notifs
;
1758 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1760 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10");
1761 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10");
1763 Scheduler::ProcessEventsToIdle();
1765 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1767 CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES
), std::get
<0>(notifs
[0]));
1768 CPPUNIT_ASSERT_EQUAL(std::string("10, 10, 30, 10"), std::get
<1>(notifs
[0]));
1770 // No part in invalidation: don't merge.
1772 std::vector
<std::tuple
<int, std::string
>> notifs
;
1773 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1775 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10");
1776 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "40, 10, 20, 10");
1778 Scheduler::ProcessEventsToIdle();
1780 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1783 // Part in invalidation, intersection and parts match -> merge.
1785 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1786 comphelper::ScopeGuard
aGuard([]()
1788 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1791 std::vector
<std::tuple
<int, std::string
>> notifs
;
1792 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1794 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0");
1795 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 0");
1797 Scheduler::ProcessEventsToIdle();
1799 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs
.size());
1801 // Part in invalidation, intersection and parts don't match -> don't merge.
1803 comphelper::LibreOfficeKit::setPartInInvalidation(true);
1804 comphelper::ScopeGuard
aGuard([]()
1806 comphelper::LibreOfficeKit::setPartInInvalidation(false);
1809 std::vector
<std::tuple
<int, std::string
>> notifs
;
1810 std::unique_ptr
<CallbackFlushHandler
> handler(new CallbackFlushHandler(pDocument
, callbackCompressionTest
, ¬ifs
));
1812 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "10, 10, 20, 10, 0");
1813 handler
->queue(LOK_CALLBACK_INVALIDATE_TILES
, "20, 10, 20, 10, 1");
1815 Scheduler::ProcessEventsToIdle();
1817 // This failed as RectangleAndPart::Create() always assumed no part in
1818 // payload, so this was merged -> it was 1.
1819 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs
.size());
1823 void DesktopLOKTest::testDialogInput()
1825 comphelper::LibreOfficeKit::setActive();
1826 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1827 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
1828 Scheduler::ProcessEventsToIdle();
1830 SfxViewShell
* pViewShell
= SfxViewShell::Current();
1831 pViewShell
->GetViewFrame()->GetBindings().Update();
1833 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
1834 CPPUNIT_ASSERT(pWindow
);
1836 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
1837 CPPUNIT_ASSERT(pCtrlFocused
);
1838 CPPUNIT_ASSERT_EQUAL(WindowType::COMBOBOX
, pCtrlFocused
->GetType());
1839 CPPUNIT_ASSERT_EQUAL(OUString(""), pCtrlFocused
->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"), pCtrlFocused
->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 CPPUNIT_ASSERT_EQUAL(OString("far beyond lovely "), OString(pText
));
1888 void DesktopLOKTest::testRedlineWriter()
1890 // Load a Writer document, enable change recording and press a key.
1891 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
1892 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1893 xPropertySet
->setPropertyValue("RecordChanges", uno::makeAny(true));
1894 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1895 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
1896 Scheduler::ProcessEventsToIdle();
1898 // Get redline info.
1899 boost::property_tree::ptree aTree
;
1900 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
1901 std::stringstream
aStream(pJSON
);
1903 CPPUNIT_ASSERT(!aStream
.str().empty());
1904 boost::property_tree::read_json(aStream
, aTree
);
1905 // Make sure that pressing a key creates exactly one redline.
1906 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
1908 for (const boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
1909 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
1910 CPPUNIT_ASSERT_EQUAL(std::string("Insert \xE2\x80\x9Ct\xE2\x80\x9D"), rRedline
.second
.get
<std::string
>("description"));
1911 // U+201C LEFT DOUBLE QUOTATION MARK, U+201D RIGHT DOUBLE QUOTATION
1915 void DesktopLOKTest::testRedlineCalc()
1917 // Load a Writer document, enable change recording and press a key.
1918 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
1919 uno::Reference
<beans::XPropertySet
> xPropertySet(mxComponent
, uno::UNO_QUERY
);
1920 xPropertySet
->setPropertyValue("RecordChanges", uno::makeAny(true));
1921 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 't', 0);
1922 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 't', 0);
1923 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RETURN
);
1924 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RETURN
);
1925 Scheduler::ProcessEventsToIdle();
1927 // Get redline info.
1928 boost::property_tree::ptree aTree
;
1929 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:AcceptTrackedChanges");
1930 std::stringstream
aStream(pJSON
);
1932 CPPUNIT_ASSERT(!aStream
.str().empty());
1933 boost::property_tree::read_json(aStream
, aTree
);
1934 // Make sure that pressing a key creates exactly one redline.
1935 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree
.get_child("redlines").size());
1937 for (const boost::property_tree::ptree::value_type
& rRedline
: aTree
.get_child("redlines"))
1938 // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
1939 CPPUNIT_ASSERT_EQUAL(std::string("Cell B4 changed from '5' to 't'"), rRedline
.second
.get
<std::string
>("description"));
1946 LibLODocument_Impl
* mpDocument
;
1949 OString m_aCellFormula
;
1950 bool m_bTilesInvalidated
;
1952 tools::Rectangle m_aOwnCursor
;
1953 boost::property_tree::ptree m_aCommentCallbackResult
;
1954 boost::property_tree::ptree m_aCallbackWindowResult
;
1955 bool m_bWindowHidden
;
1957 ViewCallback(LibLODocument_Impl
* pDocument
)
1958 : mpDocument(pDocument
),
1959 m_bTilesInvalidated(false),
1960 m_bZeroCursor(false)
1962 mnView
= SfxLokHelper::getView();
1963 mpDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, this);
1968 mpDocument
->m_pDocumentClass
->setView(mpDocument
, mnView
);
1969 mpDocument
->m_pDocumentClass
->registerCallback(mpDocument
, nullptr, nullptr);
1972 static void callback(int nType
, const char* pPayload
, void* pData
)
1974 static_cast<ViewCallback
*>(pData
)->callbackImpl(nType
, pPayload
);
1977 void callbackImpl(int nType
, const char* pPayload
)
1979 OString
aPayload(pPayload
);
1982 case LOK_CALLBACK_INVALIDATE_TILES
:
1984 m_bTilesInvalidated
= true;
1987 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1989 uno::Sequence
<OUString
> aSeq
= comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload
));
1990 if (std::string_view("EMPTY") == pPayload
)
1992 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(4), aSeq
.getLength());
1993 m_aOwnCursor
.setX(aSeq
[0].toInt32());
1994 m_aOwnCursor
.setY(aSeq
[1].toInt32());
1995 m_aOwnCursor
.setWidth(aSeq
[2].toInt32());
1996 m_aOwnCursor
.setHeight(aSeq
[3].toInt32());
1998 if (m_aOwnCursor
.getX() == 0 && m_aOwnCursor
.getY() == 0)
1999 m_bZeroCursor
= true;
2002 case LOK_CALLBACK_COMMENT
:
2004 m_aCommentCallbackResult
.clear();
2005 std::stringstream
aStream(pPayload
);
2006 boost::property_tree::read_json(aStream
, m_aCommentCallbackResult
);
2007 m_aCommentCallbackResult
= m_aCommentCallbackResult
.get_child("comment");
2010 case LOK_CALLBACK_WINDOW
:
2012 m_aCallbackWindowResult
.clear();
2013 std::stringstream
aStream(pPayload
);
2014 boost::property_tree::read_json(aStream
, m_aCallbackWindowResult
);
2016 std::string sAction
= m_aCallbackWindowResult
.get
<std::string
>("action");
2017 if (sAction
== "hide")
2018 m_bWindowHidden
= true;
2021 case LOK_CALLBACK_CELL_FORMULA
:
2023 m_aCellFormula
= aPayload
;
2032 void DesktopLOKTest::testPaintPartTile()
2034 // Load an impress doc of 2 slides.
2035 // ViewCallback aView1;
2036 // ViewCallback aView2;
2037 LibLODocument_Impl
* pDocument
= loadDoc("2slides.odp");
2038 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2039 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView1);
2040 int nView1
= pDocument
->m_pDocumentClass
->getView(pDocument
);
2042 // Create a second view.
2043 pDocument
->m_pDocumentClass
->createView(pDocument
);
2044 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2045 // pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView2);
2047 // Go to the second slide in the second view.
2048 pDocument
->m_pDocumentClass
->setPart(pDocument
, 1);
2050 // Switch back to the first view and start typing.
2051 pDocument
->m_pDocumentClass
->setView(pDocument
, nView1
);
2052 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, awt::Key::TAB
);
2053 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, awt::Key::TAB
);
2054 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2055 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2056 Scheduler::ProcessEventsToIdle();
2058 // Call paintPartTile() to paint the second part (in whichever view it finds suitable for this).
2059 unsigned char pPixels
[256 * 256 * 4];
2060 pDocument
->m_pDocumentClass
->paintPartTile(pDocument
, pPixels
, 1, 256, 256, 0, 0, 256, 256);
2063 Scheduler::ProcessEventsToIdle();
2064 // aView1.m_bTilesInvalidated = false;
2065 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'x', 0);
2066 pDocument
->m_pDocumentClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'x', 0);
2067 Scheduler::ProcessEventsToIdle();
2068 // This failed: paintPartTile() (as a side-effect) ended the text edit of
2069 // the first view, so there were no invalidations.
2070 //CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
2074 void DesktopLOKTest::testGetFontSubset()
2076 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2077 OUString aFontName
= rtl::Uri::encode(
2078 OUString("Liberation Sans"),
2079 rtl_UriCharClassRelSegment
,
2080 rtl_UriEncodeKeepEscapes
,
2081 RTL_TEXTENCODING_UTF8
2083 OString aCommand
= ".uno:FontSubset&name=" + OUStringToOString(aFontName
, RTL_TEXTENCODING_UTF8
);
2084 boost::property_tree::ptree aTree
;
2085 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, aCommand
.getStr());
2086 std::stringstream
aStream(pJSON
);
2087 boost::property_tree::read_json(aStream
, aTree
);
2088 CPPUNIT_ASSERT( !aTree
.empty() );
2089 CPPUNIT_ASSERT_EQUAL( std::string(".uno:FontSubset"), aTree
.get_child("commandName").get_value
<std::string
>() );
2090 boost::property_tree::ptree aValues
= aTree
.get_child("commandValues");
2091 CPPUNIT_ASSERT( !aValues
.empty() );
2096 void DesktopLOKTest::testCommentsWriter()
2098 // Disable tiled rendering for comments
2099 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2101 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2102 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2103 tools::Long nWidth
, nHeight
;
2104 pDocument
->m_pDocumentClass
->getDocumentSize(pDocument
, &nWidth
, &nHeight
);
2106 // Document width alongwith without sidebar comes to be < 13000
2107 CPPUNIT_ASSERT( nWidth
< 13000 );
2109 // Can we get all the comments using .uno:ViewAnnotations command ?
2110 boost::property_tree::ptree aTree
;
2111 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2112 std::stringstream
aStream(pJSON
);
2114 CPPUNIT_ASSERT(!aStream
.str().empty());
2115 boost::property_tree::read_json(aStream
, aTree
);
2116 // There are 3 comments in the document already
2117 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aTree
.get_child("comments").size());
2119 int nComment2Id
= 0;
2120 // Check if all comment fields have valid data
2121 for (const auto& rComment
: aTree
.get_child("comments"))
2123 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2124 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("author").empty());
2125 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("text").empty());
2126 // Has a valid iso 8601 date time string
2127 css::util::DateTime aDateTime
;
2128 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2129 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2131 // This comment has a marked text range
2132 if (rComment
.second
.get
<std::string
>("text") == "Comment 2")
2134 CPPUNIT_ASSERT(!rComment
.second
.get
<std::string
>("textRange").empty());
2135 nComment2Id
= rComment
.second
.get
<int>("id");
2137 // This is a reply comment
2138 else if (rComment
.second
.get
<std::string
>("text") == "Reply to Comment 2")
2140 CPPUNIT_ASSERT_EQUAL(nComment2Id
, rComment
.second
.get
<int>("parent"));
2144 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2148 void DesktopLOKTest::testCommentsCalc()
2150 // Disable tiled rendering for comments
2151 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2153 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2154 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2156 // Can we get all the comments using .uno:ViewAnnotations command ?
2157 boost::property_tree::ptree aTree
;
2158 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2159 std::stringstream
aStream(pJSON
);
2161 CPPUNIT_ASSERT(!aStream
.str().empty());
2162 boost::property_tree::read_json(aStream
, aTree
);
2163 // There are 2 comments in the document already
2164 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2166 // Check if all comment fields have valid data
2168 for (const auto& rComment
: aTree
.get_child("comments"))
2174 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2175 CPPUNIT_ASSERT_EQUAL(std::string("Comment1"), rComment
.second
.get
<std::string
>("text"));
2176 CPPUNIT_ASSERT_EQUAL(std::string("7650, 3570, 1274, 254"), rComment
.second
.get
<std::string
>("cellPos"));
2181 CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment
.second
.get
<std::string
>("tab"));
2182 CPPUNIT_ASSERT_EQUAL(std::string("Comment2"), rComment
.second
.get
<std::string
>("text"));
2183 CPPUNIT_ASSERT_EQUAL(std::string("8925, 4335, 1274, 254"), rComment
.second
.get
<std::string
>("cellPos"));
2191 // We checked all the comments
2192 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2194 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2198 void DesktopLOKTest::testCommentsImpress()
2200 // Disable tiled rendering for comments
2201 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2203 LibLODocument_Impl
* pDocument
= loadDoc("blank_presentation.odp");
2204 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, nullptr);
2206 // Can we get all the comments using .uno:ViewAnnotations command ?
2207 boost::property_tree::ptree aTree
;
2208 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2209 std::stringstream
aStream(pJSON
);
2211 CPPUNIT_ASSERT(!aStream
.str().empty());
2212 boost::property_tree::read_json(aStream
, aTree
);
2213 // There are 2 comments in the document already
2214 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree
.get_child("comments").size());
2216 // Check if all comment fields have valid data
2218 for (const auto& rComment
: aTree
.get_child("comments"))
2224 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2225 CPPUNIT_ASSERT_EQUAL(std::string("This is comment1"), rComment
.second
.get
<std::string
>("text"));
2226 CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), rComment
.second
.get
<std::string
>("author"));
2227 css::util::DateTime aDateTime
;
2228 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2229 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2234 CPPUNIT_ASSERT(rComment
.second
.get
<int>("id") > 0);
2235 CPPUNIT_ASSERT_EQUAL(std::string("This is comment2"), rComment
.second
.get
<std::string
>("text"));
2236 CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), rComment
.second
.get
<std::string
>("author"));
2237 css::util::DateTime aDateTime
;
2238 OUString aDateTimeString
= OUString::createFromAscii(rComment
.second
.get
<std::string
>("dateTime").c_str());
2239 CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString
, aDateTime
));
2247 // We checked all the comments
2248 CPPUNIT_ASSERT_EQUAL(2, nIdx
);
2250 comphelper::LibreOfficeKit::setTiledAnnotations(true);
2253 void DesktopLOKTest::testCommentsCallbacksWriter()
2255 // Comments callback are emitted only if tiled annotations are off
2256 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2257 LibLODocument_Impl
* pDocument
= loadDoc("comments.odt");
2258 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2259 ViewCallback
aView1(pDocument
);
2260 pDocument
->m_pDocumentClass
->createView(pDocument
);
2261 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2262 ViewCallback
aView2(pDocument
);
2264 // Add a new comment
2265 OString
aCommandArgs("{ \"Text\": { \"type\": \"string\", \"value\": \"Additional comment\" }, \"Author\": { \"type\": \"string\", \"value\": \"LOK User1\" } }");
2266 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2267 Scheduler::ProcessEventsToIdle();
2269 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2270 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2271 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2272 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2274 // Reply to a comment just added
2275 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment\" } }";
2276 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2277 Scheduler::ProcessEventsToIdle();
2279 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2280 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2281 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2282 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2283 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2284 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2285 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2286 int nCommentId2
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2288 // Edit the previously added comment
2289 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Edited comment\" } }";
2290 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2291 Scheduler::ProcessEventsToIdle();
2293 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2294 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2295 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2296 // parent is unchanged still
2297 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2298 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2299 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2300 CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2302 // Delete the reply comment just added
2303 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2
) + "\" } }";
2304 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteComment", aCommandArgs
.getStr(), false);
2305 Scheduler::ProcessEventsToIdle();
2307 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2308 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2309 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2310 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2311 CPPUNIT_ASSERT_EQUAL(nCommentId2
, aView2
.m_aCommentCallbackResult
.get
<int>("id"));
2313 // Reply to nCommentId1 again
2314 aCommandArgs
= "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1
) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment again\" } }";
2315 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:ReplyComment", aCommandArgs
.getStr(), false);
2316 Scheduler::ProcessEventsToIdle();
2318 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
2319 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2320 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2321 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("parent"));
2322 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView2
.m_aCommentCallbackResult
.get
<int>("parent"));
2323 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2324 CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView2
.m_aCommentCallbackResult
.get
<std::string
>("text"));
2326 // .uno:ViewAnnotations returns total of 5 comments
2327 boost::property_tree::ptree aTree
;
2328 char* pJSON
= pDocument
->m_pDocumentClass
->getCommandValues(pDocument
, ".uno:ViewAnnotations");
2329 std::stringstream
aStream(pJSON
);
2331 CPPUNIT_ASSERT(!aStream
.str().empty());
2332 boost::property_tree::read_json(aStream
, aTree
);
2333 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aTree
.get_child("comments").size());
2339 void addParameter(tools::JsonWriter
& rJson
, const char* sName
, OString
const & type
, OString
const & value
)
2341 auto testNode
= rJson
.startNode(sName
);
2342 rJson
.put("type", type
);
2343 rJson
.put("value", value
);
2348 void DesktopLOKTest::testCommentsAddEditDeleteDraw()
2350 // Comments callback are emitted only if tiled annotations are off
2351 comphelper::LibreOfficeKit::setTiledAnnotations(false);
2352 LibLODocument_Impl
* pDocument
= loadDoc("BlankDrawDocument.odg");
2353 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2354 ViewCallback
aView1(pDocument
);
2356 // Add a new comment
2357 OString aCommandArgs
;
2359 tools::JsonWriter aJson
;
2360 addParameter(aJson
, "Text", "string", "Comment");
2361 addParameter(aJson
, "Author", "string", "LOK User1");
2362 aCommandArgs
= aJson
.extractAsOString();
2365 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", aCommandArgs
.getStr(), false);
2366 Scheduler::ProcessEventsToIdle();
2368 // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
2369 CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2370 int nCommentId1
= aView1
.m_aCommentCallbackResult
.get
<int>("id");
2372 // Edit the previously added comment
2374 tools::JsonWriter aJson
;
2375 addParameter(aJson
, "Id", "string", OString::number(nCommentId1
));
2376 addParameter(aJson
, "Text", "string", "Edited comment");
2377 aCommandArgs
= aJson
.extractAsOString();
2380 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:EditAnnotation", aCommandArgs
.getStr(), false);
2381 Scheduler::ProcessEventsToIdle();
2383 // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
2384 CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2385 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2389 tools::JsonWriter aJson
;
2390 addParameter(aJson
, "Id", "string", OString::number(nCommentId1
));
2391 aCommandArgs
= aJson
.extractAsOString();
2393 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:DeleteAnnotation", aCommandArgs
.getStr(), false);
2394 Scheduler::ProcessEventsToIdle();
2396 // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
2397 CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1
.m_aCommentCallbackResult
.get
<std::string
>("action"));
2398 CPPUNIT_ASSERT_EQUAL(nCommentId1
, aView1
.m_aCommentCallbackResult
.get
<int>("id"));
2401 void DesktopLOKTest::testRunMacro()
2403 LibLibreOffice_Impl aOffice
;
2404 bool bGoodMacro
, bNonExistentMacro
;
2406 // Tools macros come pre-installed in system share/basic folder,
2407 bGoodMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, OString("macro:///Tools.Debug.ActivateReadOnlyFlag()").getStr());
2408 CPPUNIT_ASSERT(bGoodMacro
);
2410 bNonExistentMacro
= aOffice
.m_pOfficeClass
->runMacro(&aOffice
, OString("macro:///I.Am.Not(There)").getStr());
2411 CPPUNIT_ASSERT(!bNonExistentMacro
);
2414 void DesktopLOKTest::testExtractParameter()
2416 OUString
aOptions("Language=de-DE");
2417 OUString aValue
= extractParameter(aOptions
, u
"Language");
2418 CPPUNIT_ASSERT_EQUAL(OUString("de-DE"), aValue
);
2419 CPPUNIT_ASSERT_EQUAL(OUString(), aOptions
);
2421 aOptions
= "Language=en-US,Something";
2422 aValue
= extractParameter(aOptions
, u
"Language");
2423 CPPUNIT_ASSERT_EQUAL(OUString("en-US"), aValue
);
2424 CPPUNIT_ASSERT_EQUAL(OUString("Something"), aOptions
);
2426 aOptions
= "SomethingElse,Language=cs-CZ";
2427 aValue
= extractParameter(aOptions
, u
"Language");
2428 CPPUNIT_ASSERT_EQUAL(OUString("cs-CZ"), aValue
);
2429 CPPUNIT_ASSERT_EQUAL(OUString("SomethingElse"), aOptions
);
2431 aOptions
= "Something1,Language=hu-HU,Something2";
2432 aValue
= extractParameter(aOptions
, u
"Language");
2433 CPPUNIT_ASSERT_EQUAL(OUString("hu-HU"), aValue
);
2434 CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2"), aOptions
);
2436 aOptions
= "Something1,Something2=blah,Something3";
2437 aValue
= extractParameter(aOptions
, u
"Language");
2438 CPPUNIT_ASSERT_EQUAL(OUString(), aValue
);
2439 CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2=blah,Something3"), aOptions
);
2442 void DesktopLOKTest::readFileIntoByteVector(std::u16string_view sFilename
, std::vector
<unsigned char> & rByteVector
)
2444 rByteVector
.clear();
2446 createFileURL(sFilename
, aURL
);
2447 SvFileStream
aStream(aURL
, StreamMode::READ
);
2448 rByteVector
.resize(aStream
.remainingSize());
2449 aStream
.ReadBytes(rByteVector
.data(), aStream
.remainingSize());
2452 void DesktopLOKTest::testGetSignatureState_Signed()
2454 LibLODocument_Impl
* pDocument
= loadDoc("signed.odt");
2455 Scheduler::ProcessEventsToIdle();
2456 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2457 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2460 // Already SignatureState::OK, then can't test the effect of trusting new CAs.
2464 CPPUNIT_ASSERT_EQUAL(int(4), nState
);
2466 std::vector
<unsigned char> aCertificate
;
2468 readFileIntoByteVector(u
"rootCA.der", aCertificate
);
2469 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2470 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2471 CPPUNIT_ASSERT(bResult
);
2475 readFileIntoByteVector(u
"intermediateRootCA.der", aCertificate
);
2476 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2477 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2478 CPPUNIT_ASSERT(bResult
);
2481 nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2482 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2485 void DesktopLOKTest::testGetSignatureState_NonSigned()
2487 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2488 Scheduler::ProcessEventsToIdle();
2489 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2490 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2491 CPPUNIT_ASSERT_EQUAL(int(0), nState
);
2494 void DesktopLOKTest::testInsertCertificate_DER_ODT()
2496 // Load the document, save it into a temp file and load that file again
2497 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2498 utl::TempFile aTempFile
;
2499 aTempFile
.EnableKillingFile();
2500 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2503 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2505 Scheduler::ProcessEventsToIdle();
2506 CPPUNIT_ASSERT(mxComponent
.is());
2507 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2508 Scheduler::ProcessEventsToIdle();
2510 std::vector
<unsigned char> aCertificate
;
2511 std::vector
<unsigned char> aPrivateKey
;
2514 readFileIntoByteVector(u
"rootCA.der", aCertificate
);
2516 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2517 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2518 CPPUNIT_ASSERT(bResult
);
2522 readFileIntoByteVector(u
"intermediateRootCA.der", aCertificate
);
2524 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2525 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2526 CPPUNIT_ASSERT(bResult
);
2530 readFileIntoByteVector(u
"certificate.der", aCertificate
);
2531 readFileIntoByteVector(u
"certificatePrivateKey.der", aPrivateKey
);
2533 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2534 aCertificate
.data(), int(aCertificate
.size()),
2535 aPrivateKey
.data(), int(aPrivateKey
.size()));
2536 CPPUNIT_ASSERT(bResult
);
2539 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2540 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2544 void DesktopLOKTest::testInsertCertificate_PEM_ODT()
2546 // Load the document, save it into a temp file and load that file again
2547 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2548 utl::TempFile aTempFile
;
2549 aTempFile
.EnableKillingFile();
2550 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "odt", nullptr));
2553 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2555 Scheduler::ProcessEventsToIdle();
2556 CPPUNIT_ASSERT(mxComponent
.is());
2557 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2558 Scheduler::ProcessEventsToIdle();
2560 std::vector
<unsigned char> aCertificate
;
2561 std::vector
<unsigned char> aPrivateKey
;
2564 readFileIntoByteVector(u
"test-cert-chain-1.pem", aCertificate
);
2566 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2567 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2568 CPPUNIT_ASSERT(bResult
);
2572 readFileIntoByteVector(u
"test-cert-chain-2.pem", aCertificate
);
2574 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2575 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2576 CPPUNIT_ASSERT(bResult
);
2580 readFileIntoByteVector(u
"test-cert-chain-3.pem", aCertificate
);
2582 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2583 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2584 CPPUNIT_ASSERT(bResult
);
2588 readFileIntoByteVector(u
"test-cert-signing.pem", aCertificate
);
2589 readFileIntoByteVector(u
"test-PK-signing.pem", aPrivateKey
);
2591 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2592 aCertificate
.data(), int(aCertificate
.size()),
2593 aPrivateKey
.data(), int(aPrivateKey
.size()));
2594 CPPUNIT_ASSERT(bResult
);
2597 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2598 CPPUNIT_ASSERT_EQUAL(int(1), nState
);
2601 void DesktopLOKTest::testInsertCertificate_PEM_DOCX()
2603 // Load the document, save it into a temp file and load that file again
2604 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.docx");
2605 utl::TempFile aTempFile
;
2606 aTempFile
.EnableKillingFile();
2607 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "docx", nullptr));
2610 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_TEXT
);
2612 Scheduler::ProcessEventsToIdle();
2613 CPPUNIT_ASSERT(mxComponent
.is());
2614 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2615 Scheduler::ProcessEventsToIdle();
2617 std::vector
<unsigned char> aCertificate
;
2618 std::vector
<unsigned char> aPrivateKey
;
2621 readFileIntoByteVector(u
"test-cert-chain-1.pem", aCertificate
);
2623 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2624 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2625 CPPUNIT_ASSERT(bResult
);
2629 readFileIntoByteVector(u
"test-cert-chain-2.pem", aCertificate
);
2631 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2632 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2633 CPPUNIT_ASSERT(bResult
);
2637 readFileIntoByteVector(u
"test-cert-chain-3.pem", aCertificate
);
2639 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2640 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2641 CPPUNIT_ASSERT(bResult
);
2645 readFileIntoByteVector(u
"test-cert-signing.pem", aCertificate
);
2646 readFileIntoByteVector(u
"test-PK-signing.pem", aPrivateKey
);
2648 bool bResult
= pDocument
->m_pDocumentClass
->insertCertificate(pDocument
,
2649 aCertificate
.data(), int(aCertificate
.size()),
2650 aPrivateKey
.data(), int(aPrivateKey
.size()));
2651 CPPUNIT_ASSERT(bResult
);
2654 int nState
= pDocument
->m_pDocumentClass
->getSignatureState(pDocument
);
2655 CPPUNIT_ASSERT_EQUAL(int(5), nState
);
2658 void DesktopLOKTest::testSignDocument_PEM_PDF()
2660 // Load the document, save it into a temp file and load that file again
2661 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2662 utl::TempFile aTempFile
;
2663 aTempFile
.EnableKillingFile();
2665 Scheduler::ProcessEventsToIdle();
2666 CPPUNIT_ASSERT(mxComponent
.is());
2667 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2668 Scheduler::ProcessEventsToIdle();
2670 std::vector
<unsigned char> aCertificate
;
2671 std::vector
<unsigned char> aPrivateKey
;
2674 readFileIntoByteVector(u
"test-cert-chain-1.pem", aCertificate
);
2676 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2677 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2678 CPPUNIT_ASSERT(bResult
);
2682 readFileIntoByteVector(u
"test-cert-chain-2.pem", aCertificate
);
2684 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2685 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2686 CPPUNIT_ASSERT(bResult
);
2690 readFileIntoByteVector(u
"test-cert-chain-3.pem", aCertificate
);
2692 bool bResult
= pDocument
->m_pDocumentClass
->addCertificate(
2693 pDocument
, aCertificate
.data(), int(aCertificate
.size()));
2694 CPPUNIT_ASSERT(bResult
);
2697 CPPUNIT_ASSERT(pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "pdf", nullptr));
2701 Scheduler::ProcessEventsToIdle();
2703 readFileIntoByteVector(u
"test-cert-signing.pem", aCertificate
);
2704 readFileIntoByteVector(u
"test-PK-signing.pem", aPrivateKey
);
2706 LibLibreOffice_Impl aOffice
;
2707 bool bResult
= aOffice
.m_pOfficeClass
->signDocument(&aOffice
, aTempFile
.GetURL().toUtf8().getStr(),
2708 aCertificate
.data(), int(aCertificate
.size()),
2709 aPrivateKey
.data(), int(aPrivateKey
.size()));
2711 CPPUNIT_ASSERT(bResult
);
2714 void DesktopLOKTest::testTextSelectionHandles()
2716 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2717 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
2719 OString
aText("hello");
2720 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
2722 // select the inserted text
2723 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2724 Scheduler::ProcessEventsToIdle();
2725 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2726 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2728 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart
);
2729 CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd
);
2732 m_aTextSelectionStart
= "";
2733 m_aTextSelectionEnd
= "";
2734 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
2735 Scheduler::ProcessEventsToIdle();
2736 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2737 CPPUNIT_ASSERT_EQUAL(static_cast<char *>(nullptr), pText
);
2739 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionStart
);
2740 CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionEnd
);
2742 // select again; the positions of the selection handles have to be sent
2744 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2745 Scheduler::ProcessEventsToIdle();
2746 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2747 CPPUNIT_ASSERT_EQUAL(aText
, OString(pText
));
2749 CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart
);
2750 CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd
);
2753 void DesktopLOKTest::testDialogPaste()
2755 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2756 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
2757 Scheduler::ProcessEventsToIdle();
2759 SfxViewShell
* pViewShell
= SfxViewShell::Current();
2760 pViewShell
->GetViewFrame()->GetBindings().Update();
2762 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
2763 CPPUNIT_ASSERT(pWindow
);
2765 pDocument
->pClass
->postWindow(pDocument
, pWindow
->GetLOKWindowId(), LOK_WINDOW_PASTE
,
2766 "{ \"MimeType\" : { \"type\" : \"string\", \"value\" : \"text/plain;charset=utf-8\" }, \"Data\" : { \"type\" : \"[]byte\", \"value\" : \"www.softwarelibre.org.bo\" } }");
2767 Scheduler::ProcessEventsToIdle();
2769 Control
* pCtrlFocused
= GetFocusControl(pWindow
.get());
2770 CPPUNIT_ASSERT(pCtrlFocused
);
2771 CPPUNIT_ASSERT_EQUAL(WindowType::COMBOBOX
, pCtrlFocused
->GetType());
2772 CPPUNIT_ASSERT_EQUAL(OUString("www.softwarelibre.org.bo"), pCtrlFocused
->GetText());
2774 static_cast<SystemWindow
*>(pWindow
.get())->Close();
2775 Scheduler::ProcessEventsToIdle();
2778 void DesktopLOKTest::testShowHideDialog()
2781 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2783 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2784 ViewCallback
aView(pDocument
);
2786 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:HyperlinkDialog", nullptr, false);
2787 Scheduler::ProcessEventsToIdle();
2789 VclPtr
<vcl::Window
> pWindow(Application::GetActiveTopWindow());
2790 CPPUNIT_ASSERT(pWindow
);
2792 aView
.m_bWindowHidden
= false;
2795 Scheduler::ProcessEventsToIdle();
2797 CPPUNIT_ASSERT_EQUAL(true, aView
.m_bWindowHidden
);
2799 static_cast<SystemWindow
*>(pWindow
.get())->Close();
2800 Scheduler::ProcessEventsToIdle();
2803 void DesktopLOKTest::testComplexSelection()
2805 // Start with a blank text file and add contents.
2806 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
2807 static constexpr OStringLiteral
aText("hello world");
2809 // Certainly not complex.
2810 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
2813 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "text/plain;charset=utf-8", aText
.getStr(), aText
.getLength()));
2816 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE
), pDocument
->pClass
->getSelectionType(pDocument
));
2820 createFileURL(u
"paste.jpg", aFileURL
);
2821 std::ifstream
aImageStream(aFileURL
.toUtf8().copy(strlen("file://")).getStr());
2822 std::vector
<char> aImageContents((std::istreambuf_iterator
<char>(aImageStream
)), std::istreambuf_iterator
<char>());
2823 CPPUNIT_ASSERT(pDocument
->pClass
->paste(pDocument
, "image/jpeg", aImageContents
.data(), aImageContents
.size()));
2826 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SelectAll", nullptr, false);
2827 Scheduler::ProcessEventsToIdle();
2829 // Export as plain text, we should get only the text part "hello".
2830 char* pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/plain;charset=utf-8", nullptr);
2831 CPPUNIT_ASSERT(pText
!= nullptr);
2832 CPPUNIT_ASSERT_EQUAL(OString(aText
), OString(pText
));
2835 // Export as rtf, we should also get the image.
2836 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/rtf", nullptr);
2837 CPPUNIT_ASSERT(pText
!= nullptr);
2838 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
2839 CPPUNIT_ASSERT(std::string(pText
).find("pict{") != std::string::npos
); // Must have the image as well.
2842 // Export as html, we should also get the image.
2843 pText
= pDocument
->pClass
->getTextSelection(pDocument
, "text/html", nullptr);
2844 CPPUNIT_ASSERT(pText
!= nullptr);
2845 CPPUNIT_ASSERT(std::string(pText
).find(aText
.getStr()) != std::string::npos
); // Must have the text.
2846 CPPUNIT_ASSERT(std::string(pText
).find("<img") != std::string::npos
); // Must have the image as well.
2849 // We expect this to be complex.
2850 CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_COMPLEX
), pDocument
->pClass
->getSelectionType(pDocument
));
2853 void DesktopLOKTest::testCalcSaveAs()
2855 comphelper::LibreOfficeKit::setActive();
2857 LibLODocument_Impl
* pDocument
= loadDoc("sheets.ods");
2858 CPPUNIT_ASSERT(pDocument
);
2860 // Enter some text, but don't commit.
2861 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'X', 0);
2862 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 'X', 0);
2863 Scheduler::ProcessEventsToIdle();
2865 // Save as a new file.
2866 utl::TempFile aTempFile
;
2867 aTempFile
.EnableKillingFile();
2868 pDocument
->pClass
->saveAs(pDocument
, aTempFile
.GetURL().toUtf8().getStr(), "ods", nullptr);
2871 // Load the new document and verify that the in-flight changes are saved.
2872 pDocument
= loadDocUrl(aTempFile
.GetURL(), LOK_DOCTYPE_SPREADSHEET
);
2873 CPPUNIT_ASSERT(pDocument
);
2875 ViewCallback
aView(pDocument
);
2876 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
2877 pDocument
->m_pDocumentClass
->registerCallback(pDocument
, &ViewCallback::callback
, &aView
);
2879 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_RIGHT
);
2880 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_RIGHT
);
2881 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, KEY_LEFT
);
2882 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYUP
, 0, KEY_LEFT
);
2883 Scheduler::ProcessEventsToIdle();
2885 CPPUNIT_ASSERT_EQUAL(OString("X"), aView
.m_aCellFormula
);
2888 void DesktopLOKTest::testSpellcheckerMultiView()
2890 static constexpr OUStringLiteral
aLangISO(u
"en-US");
2891 SvtSysLocaleOptions aSysLocaleOptions
;
2892 aSysLocaleOptions
.SetLocaleConfigString(aLangISO
);
2893 aSysLocaleOptions
.SetUILocaleConfigString(aLangISO
);
2894 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLangISO
, true));
2896 auto aSavedSettings
= Application::GetSettings();
2897 std::unique_ptr
<Resetter
> pResetter(
2898 new Resetter([&]() { Application::SetSettings(aSavedSettings
); }));
2899 AllSettings
aSettings(aSavedSettings
);
2900 aSettings
.SetLanguageTag(aLangISO
, true);
2901 Application::SetSettings(aSettings
);
2903 LibLODocument_Impl
* pDocument
= loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET
);
2904 pDocument
->pClass
->setViewLanguage(pDocument
, 0, "en-US"); // For spellchecking.
2905 pDocument
->pClass
->initializeForRendering(pDocument
, nullptr);
2906 pDocument
->pClass
->registerCallback(pDocument
, &DesktopLOKTest::callback
, this);
2908 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2909 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2910 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
2911 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
2913 // Start spellchecking.
2914 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:SpellDialog", nullptr, false);
2916 // Uncommenting this will result in a deadlock.
2917 // Because the language configuration above is not effective, and no
2918 // language is actually set, the spell-dialog finds no misspelled
2919 // words, and displays a message box, which must be dismissed to
2921 // Need to fix the language configuration issue to enable this.
2922 // Scheduler::ProcessEventsToIdle();
2924 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2926 // Now create another view.
2927 const int nViewId
= pDocument
->m_pDocumentClass
->createView(pDocument
);
2928 CPPUNIT_ASSERT_EQUAL(2, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2931 pDocument
->m_pDocumentClass
->destroyView(pDocument
, nViewId
);
2933 // We should survive the destroyed view.
2934 CPPUNIT_ASSERT_EQUAL(1, pDocument
->m_pDocumentClass
->getViewsCount(pDocument
));
2937 void DesktopLOKTest::testMultiDocuments()
2939 for (int i
= 0; i
< 3; i
++)
2942 uno::Reference
<lang::XComponent
> xComponent1
;
2943 std::unique_ptr
<LibLODocument_Impl
> document1
;
2944 std::tie(document1
, xComponent1
) = loadDocImpl("blank_text.odt");
2945 LibLODocument_Impl
* pDocument1
= document1
.get();
2946 CPPUNIT_ASSERT_EQUAL(1, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
2947 const int nDocId1
= pDocument1
->mnDocumentId
;
2949 const int nDoc1View0
= pDocument1
->m_pDocumentClass
->getView(pDocument1
);
2950 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
2951 const int nDoc1View1
= pDocument1
->m_pDocumentClass
->createView(pDocument1
);
2952 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
2953 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
2954 CPPUNIT_ASSERT_EQUAL(2, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
2956 // Validate the views of document 1.
2957 std::vector
<int> aViewIdsDoc1(2);
2958 CPPUNIT_ASSERT(pDocument1
->m_pDocumentClass
->getViewIds(pDocument1
, aViewIdsDoc1
.data(), aViewIdsDoc1
.size()));
2959 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, aViewIdsDoc1
[0]);
2960 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, aViewIdsDoc1
[1]);
2962 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
2963 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
2964 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View0
);
2965 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
2966 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
2967 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View1
);
2968 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
2969 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
2970 CPPUNIT_ASSERT_EQUAL(2, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
2972 // Load another document.
2973 uno::Reference
<lang::XComponent
> xComponent2
;
2974 std::unique_ptr
<LibLODocument_Impl
> document2
;
2975 std::tie(document2
, xComponent2
) = loadDocImpl("blank_presentation.odp");
2976 LibLODocument_Impl
* pDocument2
= document2
.get();
2977 CPPUNIT_ASSERT_EQUAL(1, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
2978 const int nDocId2
= pDocument2
->mnDocumentId
;
2980 const int nDoc2View0
= pDocument2
->m_pDocumentClass
->getView(pDocument2
);
2981 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
2982 const int nDoc2View1
= pDocument2
->m_pDocumentClass
->createView(pDocument2
);
2983 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
2984 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
2985 CPPUNIT_ASSERT_EQUAL(2, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
2987 // Validate the views of document 2.
2988 std::vector
<int> aViewIdsDoc2(2);
2989 CPPUNIT_ASSERT(pDocument2
->m_pDocumentClass
->getViewIds(pDocument2
, aViewIdsDoc2
.data(), aViewIdsDoc2
.size()));
2990 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, aViewIdsDoc2
[0]);
2991 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, aViewIdsDoc2
[1]);
2993 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
2994 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
2995 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View0
);
2996 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
2997 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
2998 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View1
);
2999 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3000 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3001 CPPUNIT_ASSERT_EQUAL(2, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3003 // The views of document1 should be unchanged.
3004 CPPUNIT_ASSERT(pDocument1
->m_pDocumentClass
->getViewIds(pDocument1
, aViewIdsDoc1
.data(), aViewIdsDoc1
.size()));
3005 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, aViewIdsDoc1
[0]);
3006 CPPUNIT_ASSERT_EQUAL(nDoc1View1
, aViewIdsDoc1
[1]);
3007 // Switch views in the first doc.
3008 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View0
));
3009 pDocument1
->m_pDocumentClass
->setView(pDocument1
, nDoc1View0
);
3010 CPPUNIT_ASSERT_EQUAL(nDoc1View0
, pDocument1
->m_pDocumentClass
->getView(pDocument1
));
3011 CPPUNIT_ASSERT_EQUAL(nDocId1
, SfxLokHelper::getDocumentIdOfView(nDoc1View1
));
3012 pDocument1
->m_pDocumentClass
->destroyView(pDocument1
, nDoc1View1
);
3013 CPPUNIT_ASSERT_EQUAL(1, pDocument1
->m_pDocumentClass
->getViewsCount(pDocument1
));
3015 // The views of document2 should be unchanged.
3016 CPPUNIT_ASSERT(pDocument2
->m_pDocumentClass
->getViewIds(pDocument2
, aViewIdsDoc2
.data(), aViewIdsDoc2
.size()));
3017 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, aViewIdsDoc2
[0]);
3018 CPPUNIT_ASSERT_EQUAL(nDoc2View1
, aViewIdsDoc2
[1]);
3019 // Switch views in the second doc.
3020 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View0
));
3021 pDocument2
->m_pDocumentClass
->setView(pDocument2
, nDoc2View0
);
3022 CPPUNIT_ASSERT_EQUAL(nDoc2View0
, pDocument2
->m_pDocumentClass
->getView(pDocument2
));
3023 CPPUNIT_ASSERT_EQUAL(nDocId2
, SfxLokHelper::getDocumentIdOfView(nDoc2View1
));
3024 pDocument2
->m_pDocumentClass
->destroyView(pDocument2
, nDoc2View1
);
3025 CPPUNIT_ASSERT_EQUAL(1, pDocument2
->m_pDocumentClass
->getViewsCount(pDocument2
));
3027 closeDoc(document2
, xComponent2
);
3029 closeDoc(document1
, xComponent1
);
3033 void DesktopLOKTest::testControlState()
3035 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
3036 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:StarShapes", nullptr, false);
3037 Scheduler::ProcessEventsToIdle();
3039 boost::property_tree::ptree aState
;
3040 SfxViewShell
* pViewShell
= SfxViewShell::Current();
3041 pViewShell
->GetViewFrame()->GetBindings().Update();
3042 pViewShell
->GetViewFrame()->GetBindings().QueryControlState(SID_ATTR_TRANSFORM_WIDTH
, aState
);
3043 CPPUNIT_ASSERT(!aState
.empty());
3046 void DesktopLOKTest::testMetricField()
3048 LibLODocument_Impl
* pDocument
= loadDoc("search.ods");
3049 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:StarShapes", nullptr, false);
3050 Scheduler::ProcessEventsToIdle();
3052 SfxViewShell
* pViewShell
= SfxViewShell::Current();
3053 CPPUNIT_ASSERT(pViewShell
);
3055 SfxViewFrame
* pViewFrame
= pViewShell
->GetViewFrame();
3056 CPPUNIT_ASSERT(pViewFrame
);
3058 SfxChildWindow
* pSideBar
= pViewFrame
->GetChildWindow(SID_SIDEBAR
);
3059 CPPUNIT_ASSERT(pSideBar
);
3061 vcl::Window
* pWin
= pSideBar
->GetWindow();
3062 CPPUNIT_ASSERT(pWin
);
3064 WindowUIObject
aWinUI(pWin
);
3065 std::unique_ptr
<UIObject
> pUIWin(aWinUI
.get_child("selectwidth"));
3066 CPPUNIT_ASSERT(pUIWin
);
3069 aMap
["VALUE"] = "75.06";
3070 pUIWin
->execute("VALUE", aMap
);
3072 StringMap aRet
= pUIWin
->get_state();
3073 CPPUNIT_ASSERT_EQUAL(aMap
["VALUE"], aRet
["Value"]);
3076 void DesktopLOKTest::testJumpCursor()
3078 comphelper::LibreOfficeKit::setTiledAnnotations(false);
3080 LibLODocument_Impl
* pDocument
= loadDoc("blank_text.odt");
3081 pDocument
->m_pDocumentClass
->initializeForRendering(pDocument
, "{}");
3083 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'B', 0);
3084 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'o', 0);
3085 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'l', 0);
3086 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'i', 0);
3087 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'v', 0);
3088 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'i', 0);
3089 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 'a', 0);
3090 pDocument
->pClass
->postKeyEvent(pDocument
, LOK_KEYEVENT_KEYINPUT
, 0, com::sun::star::awt::Key::ESCAPE
);
3091 Scheduler::ProcessEventsToIdle();
3093 // There is a cursor jump to (0, 0) due to
3094 // mpOutlinerView->SetOutputArea( PixelToLogic( tools::Rectangle(0,0,1,1) ) );
3095 // when creating a comment
3096 ViewCallback
aView1(pDocument
);
3098 pDocument
->pClass
->postUnoCommand(pDocument
, ".uno:InsertAnnotation", nullptr, true);
3099 Scheduler::ProcessEventsToIdle();
3101 CPPUNIT_ASSERT(!aView1
.m_bZeroCursor
);
3103 comphelper::LibreOfficeKit::setTiledAnnotations(true);
3108 constexpr size_t classOffset(int i
)
3110 return sizeof(static_cast<struct _LibreOfficeKitClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
3113 constexpr size_t documentClassOffset(int i
)
3115 return sizeof(static_cast<struct _LibreOfficeKitDocumentClass
*>(nullptr)->nSize
) + i
* sizeof(void*);
3120 void DesktopLOKTest::testABI()
3122 // STABLE ABI, NEVER CHANGE (unless there's a very good reason, agreed by ESC, etc.)
3123 CPPUNIT_ASSERT_EQUAL(classOffset(0), offsetof(struct _LibreOfficeKitClass
, destroy
));
3124 CPPUNIT_ASSERT_EQUAL(classOffset(1), offsetof(struct _LibreOfficeKitClass
, documentLoad
));
3125 CPPUNIT_ASSERT_EQUAL(classOffset(2), offsetof(struct _LibreOfficeKitClass
, getError
));
3126 CPPUNIT_ASSERT_EQUAL(classOffset(3), offsetof(struct _LibreOfficeKitClass
, documentLoadWithOptions
));
3127 CPPUNIT_ASSERT_EQUAL(classOffset(4), offsetof(struct _LibreOfficeKitClass
, freeError
));
3128 CPPUNIT_ASSERT_EQUAL(classOffset(5), offsetof(struct _LibreOfficeKitClass
, registerCallback
));
3129 CPPUNIT_ASSERT_EQUAL(classOffset(6), offsetof(struct _LibreOfficeKitClass
, getFilterTypes
));
3130 CPPUNIT_ASSERT_EQUAL(classOffset(7), offsetof(struct _LibreOfficeKitClass
, setOptionalFeatures
));
3131 CPPUNIT_ASSERT_EQUAL(classOffset(8), offsetof(struct _LibreOfficeKitClass
, setDocumentPassword
));
3132 CPPUNIT_ASSERT_EQUAL(classOffset(9), offsetof(struct _LibreOfficeKitClass
, getVersionInfo
));
3133 CPPUNIT_ASSERT_EQUAL(classOffset(10), offsetof(struct _LibreOfficeKitClass
, runMacro
));
3134 CPPUNIT_ASSERT_EQUAL(classOffset(11), offsetof(struct _LibreOfficeKitClass
, signDocument
));
3136 CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass
, destroy
));
3137 CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass
, saveAs
));
3139 // Unstable ABI, but still think twice before changing this
3140 // Eg. can't you add your new member at the end of the struct instead of
3141 // in the middle? The thing you are changing - is it already part of some
3143 CPPUNIT_ASSERT_EQUAL(documentClassOffset(2), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentType
));
3144 CPPUNIT_ASSERT_EQUAL(documentClassOffset(3), offsetof(struct _LibreOfficeKitDocumentClass
, getParts
));
3145 CPPUNIT_ASSERT_EQUAL(documentClassOffset(4), offsetof(struct _LibreOfficeKitDocumentClass
, getPartPageRectangles
));
3146 CPPUNIT_ASSERT_EQUAL(documentClassOffset(5), offsetof(struct _LibreOfficeKitDocumentClass
, getPart
));
3147 CPPUNIT_ASSERT_EQUAL(documentClassOffset(6), offsetof(struct _LibreOfficeKitDocumentClass
, setPart
));
3148 CPPUNIT_ASSERT_EQUAL(documentClassOffset(7), offsetof(struct _LibreOfficeKitDocumentClass
, getPartName
));
3149 CPPUNIT_ASSERT_EQUAL(documentClassOffset(8), offsetof(struct _LibreOfficeKitDocumentClass
, setPartMode
));
3150 CPPUNIT_ASSERT_EQUAL(documentClassOffset(9), offsetof(struct _LibreOfficeKitDocumentClass
, paintTile
));
3151 CPPUNIT_ASSERT_EQUAL(documentClassOffset(10), offsetof(struct _LibreOfficeKitDocumentClass
, getTileMode
));
3152 CPPUNIT_ASSERT_EQUAL(documentClassOffset(11), offsetof(struct _LibreOfficeKitDocumentClass
, getDocumentSize
));
3153 CPPUNIT_ASSERT_EQUAL(documentClassOffset(12), offsetof(struct _LibreOfficeKitDocumentClass
, initializeForRendering
));
3154 CPPUNIT_ASSERT_EQUAL(documentClassOffset(13), offsetof(struct _LibreOfficeKitDocumentClass
, registerCallback
));
3155 CPPUNIT_ASSERT_EQUAL(documentClassOffset(14), offsetof(struct _LibreOfficeKitDocumentClass
, postKeyEvent
));
3156 CPPUNIT_ASSERT_EQUAL(documentClassOffset(15), offsetof(struct _LibreOfficeKitDocumentClass
, postMouseEvent
));
3157 CPPUNIT_ASSERT_EQUAL(documentClassOffset(16), offsetof(struct _LibreOfficeKitDocumentClass
, postUnoCommand
));
3158 CPPUNIT_ASSERT_EQUAL(documentClassOffset(17), offsetof(struct _LibreOfficeKitDocumentClass
, setTextSelection
));
3159 CPPUNIT_ASSERT_EQUAL(documentClassOffset(18), offsetof(struct _LibreOfficeKitDocumentClass
, getTextSelection
));
3160 CPPUNIT_ASSERT_EQUAL(documentClassOffset(19), offsetof(struct _LibreOfficeKitDocumentClass
, paste
));
3161 CPPUNIT_ASSERT_EQUAL(documentClassOffset(20), offsetof(struct _LibreOfficeKitDocumentClass
, setGraphicSelection
));
3162 CPPUNIT_ASSERT_EQUAL(documentClassOffset(21), offsetof(struct _LibreOfficeKitDocumentClass
, resetSelection
));
3163 CPPUNIT_ASSERT_EQUAL(documentClassOffset(22), offsetof(struct _LibreOfficeKitDocumentClass
, getCommandValues
));
3164 CPPUNIT_ASSERT_EQUAL(documentClassOffset(23), offsetof(struct _LibreOfficeKitDocumentClass
, setClientZoom
));
3165 CPPUNIT_ASSERT_EQUAL(documentClassOffset(24), offsetof(struct _LibreOfficeKitDocumentClass
, setClientVisibleArea
));
3166 CPPUNIT_ASSERT_EQUAL(documentClassOffset(25), offsetof(struct _LibreOfficeKitDocumentClass
, createView
));
3167 CPPUNIT_ASSERT_EQUAL(documentClassOffset(26), offsetof(struct _LibreOfficeKitDocumentClass
, destroyView
));
3168 CPPUNIT_ASSERT_EQUAL(documentClassOffset(27), offsetof(struct _LibreOfficeKitDocumentClass
, setView
));
3169 CPPUNIT_ASSERT_EQUAL(documentClassOffset(28), offsetof(struct _LibreOfficeKitDocumentClass
, getView
));
3170 CPPUNIT_ASSERT_EQUAL(documentClassOffset(29), offsetof(struct _LibreOfficeKitDocumentClass
, getViewsCount
));
3171 CPPUNIT_ASSERT_EQUAL(documentClassOffset(30), offsetof(struct _LibreOfficeKitDocumentClass
, renderFont
));
3172 CPPUNIT_ASSERT_EQUAL(documentClassOffset(31), offsetof(struct _LibreOfficeKitDocumentClass
, getPartHash
));
3173 CPPUNIT_ASSERT_EQUAL(documentClassOffset(32), offsetof(struct _LibreOfficeKitDocumentClass
, paintPartTile
));
3174 CPPUNIT_ASSERT_EQUAL(documentClassOffset(33), offsetof(struct _LibreOfficeKitDocumentClass
, getViewIds
));
3175 CPPUNIT_ASSERT_EQUAL(documentClassOffset(34), offsetof(struct _LibreOfficeKitDocumentClass
, setOutlineState
));
3176 CPPUNIT_ASSERT_EQUAL(documentClassOffset(35), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindow
));
3177 CPPUNIT_ASSERT_EQUAL(documentClassOffset(36), offsetof(struct _LibreOfficeKitDocumentClass
, postWindow
));
3178 CPPUNIT_ASSERT_EQUAL(documentClassOffset(37), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowKeyEvent
));
3179 CPPUNIT_ASSERT_EQUAL(documentClassOffset(38), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowMouseEvent
));
3180 CPPUNIT_ASSERT_EQUAL(documentClassOffset(39), offsetof(struct _LibreOfficeKitDocumentClass
, setViewLanguage
));
3181 CPPUNIT_ASSERT_EQUAL(documentClassOffset(40), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowExtTextInputEvent
));
3182 CPPUNIT_ASSERT_EQUAL(documentClassOffset(41), offsetof(struct _LibreOfficeKitDocumentClass
, getPartInfo
));
3183 CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowDPI
));
3184 CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass
, insertCertificate
));
3185 CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), offsetof(struct _LibreOfficeKitDocumentClass
, addCertificate
));
3186 CPPUNIT_ASSERT_EQUAL(documentClassOffset(45), offsetof(struct _LibreOfficeKitDocumentClass
, getSignatureState
));
3187 CPPUNIT_ASSERT_EQUAL(documentClassOffset(46), offsetof(struct _LibreOfficeKitDocumentClass
, renderShapeSelection
));
3188 CPPUNIT_ASSERT_EQUAL(documentClassOffset(47), offsetof(struct _LibreOfficeKitDocumentClass
, postWindowGestureEvent
));
3189 CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass
, createViewWithOptions
));
3190 CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass
, selectPart
));
3191 CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass
, moveSelectedParts
));
3192 CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass
, resizeWindow
));
3193 CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass
, getClipboard
));
3194 CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass
, setClipboard
));
3195 CPPUNIT_ASSERT_EQUAL(documentClassOffset(54), offsetof(struct _LibreOfficeKitDocumentClass
, getSelectionType
));
3196 CPPUNIT_ASSERT_EQUAL(documentClassOffset(55), offsetof(struct _LibreOfficeKitDocumentClass
, removeTextContext
));
3197 CPPUNIT_ASSERT_EQUAL(documentClassOffset(56), offsetof(struct _LibreOfficeKitDocumentClass
, sendDialogEvent
));
3198 CPPUNIT_ASSERT_EQUAL(documentClassOffset(57), offsetof(struct _LibreOfficeKitDocumentClass
, renderFontOrientation
));
3199 CPPUNIT_ASSERT_EQUAL(documentClassOffset(58), offsetof(struct _LibreOfficeKitDocumentClass
, paintWindowForView
));
3200 CPPUNIT_ASSERT_EQUAL(documentClassOffset(59), offsetof(struct _LibreOfficeKitDocumentClass
, completeFunction
));
3201 CPPUNIT_ASSERT_EQUAL(documentClassOffset(60), offsetof(struct _LibreOfficeKitDocumentClass
, setWindowTextSelection
));
3202 CPPUNIT_ASSERT_EQUAL(documentClassOffset(61), offsetof(struct _LibreOfficeKitDocumentClass
, sendFormFieldEvent
));
3204 // Extending is fine, update this, and add new assert for the offsetof the
3206 CPPUNIT_ASSERT_EQUAL(documentClassOffset(62), sizeof(struct _LibreOfficeKitDocumentClass
));
3209 CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest
);
3211 CPPUNIT_PLUGIN_IMPLEMENT();
3213 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */