summaryrefslogtreecommitdiffstats
path: root/sw/qa/extras/tiledrendering
diff options
context:
space:
mode:
Diffstat (limited to 'sw/qa/extras/tiledrendering')
-rw-r--r--sw/qa/extras/tiledrendering/data/2-pages.odtbin0 -> 8596 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/cond-coll-copy.odtbin0 -> 7351 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/double-underline_and_strike-out.fodt70
-rw-r--r--sw/qa/extras/tiledrendering/data/drop_down_form_field.odtbin0 -> 8596 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/drop_down_form_field2.odtbin0 -> 8596 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/drop_down_form_field_noitem.odtbin0 -> 8467 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/drop_down_form_field_noselection.odtbin0 -> 8512 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/dummy.fodt8
-rw-r--r--sw/qa/extras/tiledrendering/data/estonian.odtbin0 -> 11615 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/fieldmark.docxbin0 -> 13174 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/frame.odtbin0 -> 8370 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/hiddenLoremIpsum.docxbin0 -> 6807 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/hyperlink.odtbin0 -> 9154 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/image-comment.odtbin0 -> 9765 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/multiline.odtbin0 -> 10456 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/pagedown-invalidation.odtbin0 -> 8528 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/pilcrow-redlining.fodt62
-rw-r--r--sw/qa/extras/tiledrendering/data/redline-notification-during-save.odtbin0 -> 6786 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/removenode_redline_callback.fodt306
-rw-r--r--sw/qa/extras/tiledrendering/data/savedauthorfield.odtbin0 -> 9388 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/search.odtbin0 -> 11324 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/semi-transparent.odtbin0 -> 8891 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/shape-with-text.fodt17
-rw-r--r--sw/qa/extras/tiledrendering/data/shape.fodt33
-rw-r--r--sw/qa/extras/tiledrendering/data/splitnode_redline_callback.fodt294
-rw-r--r--sw/qa/extras/tiledrendering/data/table-paint-invalidate.odtbin0 -> 9067 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/tdf114799_highlight.docxbin0 -> 29040 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/tdf114799_shd.docxbin0 -> 29049 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/tdf115088.odtbin0 -> 8108 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/tdf117448.fodt30
-rw-r--r--sw/qa/extras/tiledrendering/data/tdf43244_SpacesOnMargin.odtbin0 -> 9791 bytes
-rw-r--r--sw/qa/extras/tiledrendering/data/testTableCommentRemoveCallback.odtbin0 -> 11077 bytes
-rw-r--r--sw/qa/extras/tiledrendering/tiledrendering.cxx4048
33 files changed, 4868 insertions, 0 deletions
diff --git a/sw/qa/extras/tiledrendering/data/2-pages.odt b/sw/qa/extras/tiledrendering/data/2-pages.odt
new file mode 100644
index 0000000000..04953e5edf
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/2-pages.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/cond-coll-copy.odt b/sw/qa/extras/tiledrendering/data/cond-coll-copy.odt
new file mode 100644
index 0000000000..8fa15f177e
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/cond-coll-copy.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/double-underline_and_strike-out.fodt b/sw/qa/extras/tiledrendering/data/double-underline_and_strike-out.fodt
new file mode 100644
index 0000000000..881143bdae
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/double-underline_and_strike-out.fodt
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:settings>
+ <config:config-item-set config:name="ooo:view-settings">
+ <config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="ViewAreaWidth" config:type="long">35008</config:config-item>
+ <config:config-item config:name="ViewAreaHeight" config:type="long">27855</config:config-item>
+ <config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
+ <config:config-item-map-indexed config:name="Views">
+ <config:config-item-map-entry>
+ <config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
+ <config:config-item config:name="ViewLeft" config:type="long">18905</config:config-item>
+ <config:config-item config:name="ViewTop" config:type="long">3475</config:config-item>
+ <config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleRight" config:type="long">35006</config:config-item>
+ <config:config-item config:name="VisibleBottom" config:type="long">27854</config:config-item>
+ <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
+ <config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
+ <config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ZoomFactor" config:type="short">87</config:config-item>
+ <config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="HideWhitespace" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
+ </config:config-item-map-entry>
+ </config:config-item-map-indexed>
+ </config:config-item-set>
+ </office:settings>
+ <office:styles>
+ <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
+ <style:default-style style:family="paragraph">
+ <style:text-properties fo:language="en" fo:country="US"/>
+ </style:default-style>
+ </office:styles>
+ <office:body>
+ <office:text>
+ <text:tracked-changes text:track-changes="false">
+ <text:changed-region xml:id="ct94876403824720" text:id="ct94876403824720">
+ <text:deletion>
+ <office:change-info>
+ <dc:creator>Unknown Author</dc:creator>
+ <dc:date>2022-11-28T10:18:08</dc:date>
+ </office:change-info>
+ </text:deletion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct94876402741808" text:id="ct94876402741808">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>Unknown Author</dc:creator>
+ <dc:date>2022-11-28T10:18:08</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ </text:tracked-changes>
+ <text:sequence-decls>
+ <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+ </text:sequence-decls>
+ <text:p text:style-name="Standard"><text:change-start text:change-id="ct94876403824720"/><text:tab/><text:tab/><text:tab/><text:tab/><text:tab/><text:tab/><text:span text:style-name="T1">Lorem ipsum…</text:span><text:change-end text:change-id="ct94876403824720"/></text:p>
+ <text:p text:style-name="P1"><text:tab/><text:tab/><text:tab/><text:tab/><text:tab/><text:tab/><text:span text:style-name="T1">Dolor…</text:span></text:p>
+ <text:p text:style-name="P1"><text:change-start text:change-id="ct94876402741808"/><text:tab/><text:tab/><text:tab/><text:tab/><text:tab/><text:tab/><text:span text:style-name="T1">Lorem ipsum…</text:span><text:change-end text:change-id="ct94876402741808"/></text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/tiledrendering/data/drop_down_form_field.odt b/sw/qa/extras/tiledrendering/data/drop_down_form_field.odt
new file mode 100644
index 0000000000..7793aff4e9
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/drop_down_form_field.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/drop_down_form_field2.odt b/sw/qa/extras/tiledrendering/data/drop_down_form_field2.odt
new file mode 100644
index 0000000000..7793aff4e9
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/drop_down_form_field2.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/drop_down_form_field_noitem.odt b/sw/qa/extras/tiledrendering/data/drop_down_form_field_noitem.odt
new file mode 100644
index 0000000000..c0b703320b
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/drop_down_form_field_noitem.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/drop_down_form_field_noselection.odt b/sw/qa/extras/tiledrendering/data/drop_down_form_field_noselection.odt
new file mode 100644
index 0000000000..0c433c6470
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/drop_down_form_field_noselection.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/dummy.fodt b/sw/qa/extras/tiledrendering/data/dummy.fodt
new file mode 100644
index 0000000000..79a34d5143
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/dummy.fodt
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+ <office:text>
+ <text:p>Aaa bbb.</text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/tiledrendering/data/estonian.odt b/sw/qa/extras/tiledrendering/data/estonian.odt
new file mode 100644
index 0000000000..0d1fedd6ba
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/estonian.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/fieldmark.docx b/sw/qa/extras/tiledrendering/data/fieldmark.docx
new file mode 100644
index 0000000000..02748bb7e1
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/fieldmark.docx
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/frame.odt b/sw/qa/extras/tiledrendering/data/frame.odt
new file mode 100644
index 0000000000..167e8f5b10
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/frame.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/hiddenLoremIpsum.docx b/sw/qa/extras/tiledrendering/data/hiddenLoremIpsum.docx
new file mode 100644
index 0000000000..0802f6e7d3
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/hiddenLoremIpsum.docx
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/hyperlink.odt b/sw/qa/extras/tiledrendering/data/hyperlink.odt
new file mode 100644
index 0000000000..4a97bf76b6
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/hyperlink.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/image-comment.odt b/sw/qa/extras/tiledrendering/data/image-comment.odt
new file mode 100644
index 0000000000..0852bedabf
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/image-comment.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/multiline.odt b/sw/qa/extras/tiledrendering/data/multiline.odt
new file mode 100644
index 0000000000..4c60b58dec
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/multiline.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/pagedown-invalidation.odt b/sw/qa/extras/tiledrendering/data/pagedown-invalidation.odt
new file mode 100644
index 0000000000..0cad2d270a
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/pagedown-invalidation.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/pilcrow-redlining.fodt b/sw/qa/extras/tiledrendering/data/pilcrow-redlining.fodt
new file mode 100644
index 0000000000..5f7bd03168
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/pilcrow-redlining.fodt
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+ <office:text>
+ <text:tracked-changes text:track-changes="false">
+ <text:changed-region xml:id="ct94231417949824" text:id="ct94231417949824">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>NL</dc:creator>
+ <dc:date>2020-11-03T09:00:05</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct94231385489296" text:id="ct94231385489296">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>NL</dc:creator>
+ <dc:date>2020-11-03T08:56:53</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct94231417752384" text:id="ct94231417752384">
+ <text:deletion>
+ <office:change-info>
+ <dc:creator>NL</dc:creator>
+ <dc:date>2020-11-03T09:00:02</dc:date>
+ </office:change-info>
+ </text:deletion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct94231417929984" text:id="ct94231417929984">
+ <text:deletion>
+ <office:change-info>
+ <dc:creator>NL</dc:creator>
+ <dc:date>2020-11-03T08:57:37</dc:date>
+ </office:change-info>
+ </text:deletion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct94231418119440" text:id="ct94231418119440">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>NL</dc:creator>
+ <dc:date>2020-11-03T09:00:18</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct94231417325888" text:id="ct94231417325888">
+ <text:deletion>
+ <office:change-info>
+ <dc:creator>NL</dc:creator>
+ <dc:date>2020-11-03T09:00:21</dc:date>
+ </office:change-info>
+ </text:deletion>
+ </text:changed-region>
+ </text:tracked-changes>
+ <text:p text:style-name="P1"><text:span text:style-name="T1">insert paragraph breaks</text:span><text:change-start text:change-id="ct94231417949824"/></text:p>
+ <text:p text:style-name="P1"><text:change-end text:change-id="ct94231417949824"/><text:change-start text:change-id="ct94231385489296"/></text:p>
+ <text:p text:style-name="P1"><text:change-end text:change-id="ct94231385489296"/><text:span text:style-name="T1">delete paragraph breaks</text:span><text:change-start text:change-id="ct94231417752384"/></text:p>
+ <text:p text:style-name="P1"><text:change-end text:change-id="ct94231417752384"/><text:change-start text:change-id="ct94231417929984"/></text:p>
+ <text:p text:style-name="P2"><text:change-end text:change-id="ct94231417929984"/>insert line breaks<text:change-start text:change-id="ct94231418119440"/><text:line-break/><text:line-break/><text:change-end text:change-id="ct94231418119440"/>delete line breaks<text:change-start text:change-id="ct94231417325888"/><text:line-break/><text:line-break/><text:change-end text:change-id="ct94231417325888"/></text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/tiledrendering/data/redline-notification-during-save.odt b/sw/qa/extras/tiledrendering/data/redline-notification-during-save.odt
new file mode 100644
index 0000000000..df4c30682a
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/redline-notification-during-save.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/removenode_redline_callback.fodt b/sw/qa/extras/tiledrendering/data/removenode_redline_callback.fodt
new file mode 100644
index 0000000000..ed06348cc4
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/removenode_redline_callback.fodt
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:meta><meta:creation-date>2016-05-17T06:56:18.472497792</meta:creation-date><meta:generator>LibreOfficeDev/6.1.0.0.alpha0$Linux_X86_64 LibreOffice_project/8b89db9127aaac53e19b884c3d546be4127ee38e</meta:generator><meta:editing-duration>PT1H57M33S</meta:editing-duration><meta:editing-cycles>32</meta:editing-cycles><dc:date>2018-03-05T02:20:39.602490321</dc:date><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="2" meta:word-count="2" meta:character-count="8" meta:non-whitespace-character-count="8"/></office:meta>
+ <office:settings>
+ <config:config-item-set config:name="ooo:view-settings">
+ <config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="ViewAreaWidth" config:type="long">32228</config:config-item>
+ <config:config-item config:name="ViewAreaHeight" config:type="long">20348</config:config-item>
+ <config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
+ <config:config-item-map-indexed config:name="Views">
+ <config:config-item-map-entry>
+ <config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
+ <config:config-item config:name="ViewLeft" config:type="long">7613</config:config-item>
+ <config:config-item config:name="ViewTop" config:type="long">5962</config:config-item>
+ <config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleRight" config:type="long">32226</config:config-item>
+ <config:config-item config:name="VisibleBottom" config:type="long">20346</config:config-item>
+ <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
+ <config:config-item config:name="ViewLayoutColumns" config:type="short">1</config:config-item>
+ <config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
+ <config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
+ </config:config-item-map-entry>
+ </config:config-item-map-indexed>
+ </config:config-item-set>
+ <config:config-item-set config:name="ooo:configuration-settings">
+ <config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
+ <config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintFaxName" config:type="string"/>
+ <config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="DisableOffPagePositioning" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabOverflow" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SmallCapsPercentage66" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="RsidRoot" config:type="int">24907</config:config-item>
+ <config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterName" config:type="string"/>
+ <config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
+ <config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="Rsid" config:type="int">1961503</config:config-item>
+ <config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
+ <config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
+ <config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="MathBaselineAlignment" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
+ <config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
+ <config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
+ <config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
+ <config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
+ <config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
+ <config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
+ </config:config-item-set>
+ </office:settings>
+ <office:scripts>
+ <office:script script:language="ooo:Basic">
+ <ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
+ </office:script>
+ </office:scripts>
+ <office:font-face-decls>
+ <style:font-face style:name="FreeSans1" svg:font-family="FreeSans" style:font-family-generic="swiss"/>
+ <style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
+ <style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
+ <style:font-face style:name="Open Sans" svg:font-family="&apos;Open Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
+ <style:font-face style:name="Droid Sans" svg:font-family="&apos;Droid Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
+ <style:font-face style:name="FreeSans" svg:font-family="FreeSans" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+ <style:default-style style:family="graphic">
+ <style:graphic-properties svg:stroke-color="#808080" draw:fill-color="#cfe7f5" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.1181in" draw:shadow-offset-y="0.1181in" draw:start-line-spacing-horizontal="0.1114in" draw:start-line-spacing-vertical="0.1114in" draw:end-line-spacing-horizontal="0.1114in" draw:end-line-spacing-vertical="0.1114in" style:flow-with-text="false"/>
+ <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:font-independent-line-spacing="false">
+ <style:tab-stops/>
+ </style:paragraph-properties>
+ <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Droid Sans" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="FreeSans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
+ </style:default-style>
+ <style:default-style style:family="paragraph">
+ <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="0.4925in" style:writing-mode="page"/>
+ <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Droid Sans" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="FreeSans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+ </style:default-style>
+ <style:default-style style:family="table">
+ <style:table-properties table:border-model="collapsing"/>
+ </style:default-style>
+ <style:default-style style:family="table-row">
+ <style:table-row-properties fo:keep-together="auto"/>
+ </style:default-style>
+ <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
+ <style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
+ <style:paragraph-properties fo:margin-top="0.1665in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false" fo:keep-with-next="always"/>
+ <style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="14pt" style:font-name-asian="Droid Sans" style:font-family-asian="&apos;Droid Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="FreeSans" style:font-family-complex="FreeSans" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
+ </style:style>
+ <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
+ <style:paragraph-properties fo:margin-top="0in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false"/>
+ </style:style>
+ <style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
+ <style:text-properties style:font-size-asian="12pt" style:font-name-complex="FreeSans1" style:font-family-complex="FreeSans" style:font-family-generic-complex="swiss"/>
+ </style:style>
+ <style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
+ <style:paragraph-properties fo:margin-top="0.0835in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
+ <style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-name-complex="FreeSans1" style:font-family-complex="FreeSans" style:font-family-generic-complex="swiss" style:font-size-complex="12pt" style:font-style-complex="italic"/>
+ </style:style>
+ <style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
+ <style:paragraph-properties text:number-lines="false" text:line-number="0"/>
+ <style:text-properties style:font-size-asian="12pt" style:font-name-complex="FreeSans1" style:font-family-complex="FreeSans" style:font-family-generic-complex="swiss"/>
+ </style:style>
+ <text:outline-style style:name="Outline">
+ <text:outline-level-style text:level="1" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.3in" fo:text-indent="-0.3in" fo:margin-left="0.3in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="2" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.4in" fo:text-indent="-0.4in" fo:margin-left="0.4in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="3" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.5in" fo:text-indent="-0.5in" fo:margin-left="0.5in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="4" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.6in" fo:text-indent="-0.6in" fo:margin-left="0.6in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="5" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.7in" fo:text-indent="-0.7in" fo:margin-left="0.7in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="6" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.8in" fo:text-indent="-0.8in" fo:margin-left="0.8in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="7" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.9in" fo:text-indent="-0.9in" fo:margin-left="0.9in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="8" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="1in" fo:text-indent="-1in" fo:margin-left="1in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="9" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="1.1in" fo:text-indent="-1.1in" fo:margin-left="1.1in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="10" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="1.2in" fo:text-indent="-1.2in" fo:margin-left="1.2in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ </text:outline-style>
+ <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
+ <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
+ <text:linenumbering-configuration text:number-lines="false" text:offset="0.1965in" style:num-format="1" text:number-position="left" text:increment="5"/>
+ </office:styles>
+ <office:automatic-styles>
+ <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="0000614b" officeooo:paragraph-rsid="0000614b" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P2" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="00054216" officeooo:paragraph-rsid="00054216" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P3" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="001b47cb" officeooo:paragraph-rsid="001b47cb" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P4" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="001b47cb" officeooo:paragraph-rsid="0000614b" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P5" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="00054216" officeooo:paragraph-rsid="00054216" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P6" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="0000614b" officeooo:paragraph-rsid="0000614b" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:page-layout style:name="pm1">
+ <style:page-layout-properties fo:page-width="8.2681in" fo:page-height="11.6929in" style:num-format="1" style:print-orientation="portrait" fo:margin-top="0.7874in" fo:margin-bottom="0.7874in" fo:margin-left="0.7874in" fo:margin-right="0.7874in" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.278in" style:layout-grid-ruby-height="0.139in" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0in">
+ <style:footnote-sep style:width="0.0071in" style:distance-before-sep="0.0398in" style:distance-after-sep="0.0398in" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
+ </style:page-layout-properties>
+ <style:header-style/>
+ <style:footer-style/>
+ </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+ <office:text>
+ <office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
+ <text:tracked-changes text:track-changes="false">
+ <text:changed-region xml:id="ct148588784" text:id="ct148588784">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>Aron Budea</dc:creator>
+ <dc:date>2018-02-23T13:32:12</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct148756000" text:id="ct148756000">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>Aron Budea</dc:creator>
+ <dc:date>2018-02-23T13:32:15</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ </text:tracked-changes>
+ <text:sequence-decls>
+ <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+ </text:sequence-decls>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"><text:change-start text:change-id="ct148588784"/>abcd<text:change-end text:change-id="ct148588784"/></text:p>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P2"><text:change-start text:change-id="ct148756000"/>abcd<text:change-end text:change-id="ct148756000"/></text:p>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"/>
+ </office:text>
+ </office:body>
+</office:document> \ No newline at end of file
diff --git a/sw/qa/extras/tiledrendering/data/savedauthorfield.odt b/sw/qa/extras/tiledrendering/data/savedauthorfield.odt
new file mode 100644
index 0000000000..a060c5892f
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/savedauthorfield.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/search.odt b/sw/qa/extras/tiledrendering/data/search.odt
new file mode 100644
index 0000000000..76ab2e1936
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/search.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/semi-transparent.odt b/sw/qa/extras/tiledrendering/data/semi-transparent.odt
new file mode 100644
index 0000000000..eb76980e74
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/semi-transparent.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/shape-with-text.fodt b/sw/qa/extras/tiledrendering/data/shape-with-text.fodt
new file mode 100644
index 0000000000..ce76aaf1b5
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/shape-with-text.fodt
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+ <office:text>
+ <text:sequence-decls>
+ <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+ </text:sequence-decls>
+ <text:p><draw:custom-shape text:anchor-type="paragraph" draw:z-index="0" svg:width="4.883cm" svg:height="3.225cm" svg:x="2.602cm" svg:y="1.178cm">
+ <text:p>Shape text</text:p>
+ <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:glue-points="10800 0 3163 3163 0 10800 3163 18437 10800 21600 18437 18437 21600 10800 18437 3163" draw:text-areas="3163 3163 18437 18437" draw:type="ellipse" draw:enhanced-path="U 10800 10800 10800 10800 0 360 Z N"/>
+ </draw:custom-shape>Hello.</text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/tiledrendering/data/shape.fodt b/sw/qa/extras/tiledrendering/data/shape.fodt
new file mode 100644
index 0000000000..4e9a7f629b
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/shape.fodt
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:styles>
+ <style:default-style style:family="graphic">
+ <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
+ <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
+ <style:tab-stops/>
+ </style:paragraph-properties>
+ <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="GB" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Droid Sans Devanagari1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
+ </style:default-style>
+ </office:styles>
+ <office:automatic-styles>
+ <style:style style:name="gr1" style:family="graphic">
+ <style:graphic-properties draw:textarea-horizontal-align="justify" draw:textarea-vertical-align="middle" draw:auto-grow-height="false" fo:min-height="1.9cm" fo:min-width="3.471cm" style:run-through="foreground" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="from-top" style:vertical-rel="paragraph" style:horizontal-pos="from-left" style:horizontal-rel="paragraph"/>
+ </style:style>
+ </office:automatic-styles>
+ <office:body>
+ <office:text>
+ <text:sequence-decls>
+ <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+ </text:sequence-decls>
+ <text:p text:style-name="Standard"><draw:custom-shape text:anchor-type="paragraph" draw:z-index="0" draw:name="Shape1" draw:style-name="gr1" svg:width="4.908cm" svg:height="2.687cm" svg:x="1.575cm" svg:y="-0.132cm">
+ <text:p/>
+ <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:glue-points="10800 0 3163 3163 0 10800 3163 18437 10800 21600 18437 18437 21600 10800 18437 3163" draw:text-areas="3163 3163 18437 18437" draw:type="ellipse" draw:enhanced-path="U 10800 10800 10800 10800 0 360 Z N"/>
+ </draw:custom-shape>Hello.</text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/tiledrendering/data/splitnode_redline_callback.fodt b/sw/qa/extras/tiledrendering/data/splitnode_redline_callback.fodt
new file mode 100644
index 0000000000..bffc3a42c9
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/splitnode_redline_callback.fodt
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:meta><meta:creation-date>2016-05-17T06:56:18.472497792</meta:creation-date><meta:generator>LibreOfficeDev/6.1.0.0.alpha0$Linux_X86_64 LibreOffice_project/d64ce643275e0b2b0dea9e532fc261391dc8793c</meta:generator><meta:editing-duration>PT1H57M11S</meta:editing-duration><meta:editing-cycles>31</meta:editing-cycles><dc:date>2018-03-01T05:44:45.452664252</dc:date><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="2" meta:word-count="2" meta:character-count="8" meta:non-whitespace-character-count="8"/></office:meta>
+ <office:settings>
+ <config:config-item-set config:name="ooo:view-settings">
+ <config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="ViewAreaWidth" config:type="long">32228</config:config-item>
+ <config:config-item config:name="ViewAreaHeight" config:type="long">20348</config:config-item>
+ <config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
+ <config:config-item-map-indexed config:name="Views">
+ <config:config-item-map-entry>
+ <config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
+ <config:config-item config:name="ViewLeft" config:type="long">7613</config:config-item>
+ <config:config-item config:name="ViewTop" config:type="long">8846</config:config-item>
+ <config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleRight" config:type="long">32226</config:config-item>
+ <config:config-item config:name="VisibleBottom" config:type="long">20346</config:config-item>
+ <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
+ <config:config-item config:name="ViewLayoutColumns" config:type="short">1</config:config-item>
+ <config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
+ <config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
+ </config:config-item-map-entry>
+ </config:config-item-map-indexed>
+ </config:config-item-set>
+ <config:config-item-set config:name="ooo:configuration-settings">
+ <config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
+ <config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintFaxName" config:type="string"/>
+ <config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="DisableOffPagePositioning" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabOverflow" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SmallCapsPercentage66" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="RsidRoot" config:type="int">24907</config:config-item>
+ <config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterName" config:type="string"/>
+ <config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
+ <config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="Rsid" config:type="int">1952207</config:config-item>
+ <config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
+ <config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
+ <config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="MathBaselineAlignment" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
+ <config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
+ <config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
+ <config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
+ <config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
+ <config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
+ <config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
+ </config:config-item-set>
+ </office:settings>
+ <office:scripts>
+ <office:script script:language="ooo:Basic">
+ <ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
+ </office:script>
+ </office:scripts>
+ <office:font-face-decls>
+ <style:font-face style:name="FreeSans1" svg:font-family="FreeSans" style:font-family-generic="swiss"/>
+ <style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
+ <style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
+ <style:font-face style:name="Open Sans" svg:font-family="&apos;Open Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
+ <style:font-face style:name="Droid Sans" svg:font-family="&apos;Droid Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
+ <style:font-face style:name="FreeSans" svg:font-family="FreeSans" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+ <style:default-style style:family="graphic">
+ <style:graphic-properties svg:stroke-color="#808080" draw:fill-color="#cfe7f5" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.1181in" draw:shadow-offset-y="0.1181in" draw:start-line-spacing-horizontal="0.1114in" draw:start-line-spacing-vertical="0.1114in" draw:end-line-spacing-horizontal="0.1114in" draw:end-line-spacing-vertical="0.1114in" style:flow-with-text="false"/>
+ <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:font-independent-line-spacing="false">
+ <style:tab-stops/>
+ </style:paragraph-properties>
+ <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Droid Sans" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="FreeSans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
+ </style:default-style>
+ <style:default-style style:family="paragraph">
+ <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="0.4925in" style:writing-mode="page"/>
+ <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Droid Sans" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="FreeSans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+ </style:default-style>
+ <style:default-style style:family="table">
+ <style:table-properties table:border-model="collapsing"/>
+ </style:default-style>
+ <style:default-style style:family="table-row">
+ <style:table-row-properties fo:keep-together="auto"/>
+ </style:default-style>
+ <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
+ <style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
+ <style:paragraph-properties fo:margin-top="0.1665in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false" fo:keep-with-next="always"/>
+ <style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="14pt" style:font-name-asian="Droid Sans" style:font-family-asian="&apos;Droid Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="FreeSans" style:font-family-complex="FreeSans" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
+ </style:style>
+ <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
+ <style:paragraph-properties fo:margin-top="0in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false"/>
+ </style:style>
+ <style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
+ <style:text-properties style:font-size-asian="12pt" style:font-name-complex="FreeSans1" style:font-family-complex="FreeSans" style:font-family-generic-complex="swiss"/>
+ </style:style>
+ <style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
+ <style:paragraph-properties fo:margin-top="0.0835in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
+ <style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-name-complex="FreeSans1" style:font-family-complex="FreeSans" style:font-family-generic-complex="swiss" style:font-size-complex="12pt" style:font-style-complex="italic"/>
+ </style:style>
+ <style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
+ <style:paragraph-properties text:number-lines="false" text:line-number="0"/>
+ <style:text-properties style:font-size-asian="12pt" style:font-name-complex="FreeSans1" style:font-family-complex="FreeSans" style:font-family-generic-complex="swiss"/>
+ </style:style>
+ <text:outline-style style:name="Outline">
+ <text:outline-level-style text:level="1" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.3in" fo:text-indent="-0.3in" fo:margin-left="0.3in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="2" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.4in" fo:text-indent="-0.4in" fo:margin-left="0.4in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="3" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.5in" fo:text-indent="-0.5in" fo:margin-left="0.5in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="4" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.6in" fo:text-indent="-0.6in" fo:margin-left="0.6in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="5" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.7in" fo:text-indent="-0.7in" fo:margin-left="0.7in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="6" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.8in" fo:text-indent="-0.8in" fo:margin-left="0.8in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="7" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="0.9in" fo:text-indent="-0.9in" fo:margin-left="0.9in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="8" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="1in" fo:text-indent="-1in" fo:margin-left="1in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="9" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="1.1in" fo:text-indent="-1.1in" fo:margin-left="1.1in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ <text:outline-level-style text:level="10" style:num-format="">
+ <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
+ <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="1.2in" fo:text-indent="-1.2in" fo:margin-left="1.2in"/>
+ </style:list-level-properties>
+ </text:outline-level-style>
+ </text:outline-style>
+ <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
+ <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
+ <text:linenumbering-configuration text:number-lines="false" text:offset="0.1965in" style:num-format="1" text:number-position="left" text:increment="5"/>
+ </office:styles>
+ <office:automatic-styles>
+ <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="0000614b" officeooo:paragraph-rsid="0000614b" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P2" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="00054216" officeooo:paragraph-rsid="00054216" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P3" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="001b47cb" officeooo:paragraph-rsid="001b47cb" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P4" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="001b47cb" officeooo:paragraph-rsid="0000614b" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:style style:name="P5" style:family="paragraph" style:parent-style-name="Standard">
+ <style:text-properties style:font-name="Open Sans" fo:font-size="12pt" officeooo:rsid="00054216" officeooo:paragraph-rsid="00054216" style:font-size-asian="10.5pt" style:font-size-complex="12pt"/>
+ </style:style>
+ <style:page-layout style:name="pm1">
+ <style:page-layout-properties fo:page-width="8.2681in" fo:page-height="11.6929in" style:num-format="1" style:print-orientation="portrait" fo:margin-top="0.7874in" fo:margin-bottom="0.7874in" fo:margin-left="0.7874in" fo:margin-right="0.7874in" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.278in" style:layout-grid-ruby-height="0.139in" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0in">
+ <style:footnote-sep style:width="0.0071in" style:distance-before-sep="0.0398in" style:distance-after-sep="0.0398in" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
+ </style:page-layout-properties>
+ <style:header-style/>
+ <style:footer-style/>
+ </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+ <office:text>
+ <office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
+ <text:tracked-changes text:track-changes="false">
+ <text:changed-region xml:id="ct47155616" text:id="ct47155616">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>Aron Budea</dc:creator>
+ <dc:date>2018-02-23T13:32:12</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ <text:changed-region xml:id="ct37269280" text:id="ct37269280">
+ <text:insertion>
+ <office:change-info>
+ <dc:creator>Aron Budea</dc:creator>
+ <dc:date>2018-02-23T13:32:15</dc:date>
+ </office:change-info>
+ </text:insertion>
+ </text:changed-region>
+ </text:tracked-changes>
+ <text:sequence-decls>
+ <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+ </text:sequence-decls>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"><text:change-start text:change-id="ct47155616"/>abcd<text:change-end text:change-id="ct47155616"/></text:p>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P1"/>
+ <text:p text:style-name="P2"><text:change-start text:change-id="ct37269280"/>abcd<text:change-end text:change-id="ct37269280"/></text:p>
+ <text:p text:style-name="P2"/>
+ <text:p text:style-name="P2"/>
+ </office:text>
+ </office:body>
+</office:document> \ No newline at end of file
diff --git a/sw/qa/extras/tiledrendering/data/table-paint-invalidate.odt b/sw/qa/extras/tiledrendering/data/table-paint-invalidate.odt
new file mode 100644
index 0000000000..b42c5cc515
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/table-paint-invalidate.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/tdf114799_highlight.docx b/sw/qa/extras/tiledrendering/data/tdf114799_highlight.docx
new file mode 100644
index 0000000000..3a64d71d7c
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/tdf114799_highlight.docx
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/tdf114799_shd.docx b/sw/qa/extras/tiledrendering/data/tdf114799_shd.docx
new file mode 100644
index 0000000000..730ef91dce
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/tdf114799_shd.docx
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/tdf115088.odt b/sw/qa/extras/tiledrendering/data/tdf115088.odt
new file mode 100644
index 0000000000..b29681eb8e
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/tdf115088.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/tdf117448.fodt b/sw/qa/extras/tiledrendering/data/tdf117448.fodt
new file mode 100644
index 0000000000..20846d8cdd
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/tdf117448.fodt
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:styles>
+ <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
+ <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
+ <style:paragraph-properties fo:margin-top="0pt" fo:margin-bottom="7pt" loext:contextual-spacing="false" fo:line-height="115%"/>
+ </style:style>
+ </office:styles>
+ <office:automatic-styles>
+ <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard">
+ <style:paragraph-properties fo:margin-top="8pt" fo:margin-bottom="8pt" loext:contextual-spacing="false" fo:line-height="2pt"/>
+ <style:text-properties fo:color="#000000" loext:opacity="100%" style:font-name="Liberation Serif" fo:font-size="12pt"/>
+ </style:style>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+ <office:text>
+ <table:table table:name="Táblázat1" table:style-name="Táblázat1">
+ <table:table-column table:style-name="Táblázat1.A"/>
+ <table:table-row>
+ <table:table-cell table:style-name="Táblázat1.A1" office:value-type="string">
+ <text:p text:style-name="P1">Text without clipping.</text:p>
+ </table:table-cell>
+ </table:table-row>
+ </table:table>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/tiledrendering/data/tdf43244_SpacesOnMargin.odt b/sw/qa/extras/tiledrendering/data/tdf43244_SpacesOnMargin.odt
new file mode 100644
index 0000000000..76c293dc0b
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/tdf43244_SpacesOnMargin.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/data/testTableCommentRemoveCallback.odt b/sw/qa/extras/tiledrendering/data/testTableCommentRemoveCallback.odt
new file mode 100644
index 0000000000..09941166a7
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/data/testTableCommentRemoveCallback.odt
Binary files differ
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx
new file mode 100644
index 0000000000..2325ce93b4
--- /dev/null
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -0,0 +1,4048 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <swmodeltestbase.hxx>
+
+#include <string>
+#include <string_view>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/text/AuthorDisplayFormat.hpp>
+#include <com/sun/star/datatransfer/XTransferable2.hpp>
+
+#include <test/helper/transferable.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdview.hxx>
+#include <vcl/virdev.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/wghtitem.hxx>
+#include <svl/srchitem.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/voiditem.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <vcl/scheduler.hxx>
+#include <vcl/vclevent.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/ITiledRenderable.hxx>
+#include <tools/json_writer.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <test/lokcallback.hxx>
+
+#include <drawdoc.hxx>
+#include <ndtxt.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <UndoManager.hxx>
+#include <cmdid.h>
+#include <redline.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <flddat.hxx>
+#include <basesh.hxx>
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <fmtanchr.hxx>
+#include <textcontentcontrol.hxx>
+#include <swmodule.hxx>
+#include <swdll.hxx>
+
+static std::ostream& operator<<(std::ostream& os, ViewShellId id)
+{
+ os << static_cast<sal_Int32>(id);
+ return os;
+}
+
+/// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface.
+class SwTiledRenderingTest : public SwModelTestBase
+{
+public:
+ SwTiledRenderingTest();
+ virtual void setUp() override;
+ virtual void tearDown() override;
+
+protected:
+ SwXTextDocument* createDoc(const char* pName = nullptr);
+ void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
+ static void callback(int nType, const char* pPayload, void* pData);
+ void callbackImpl(int nType, const char* pPayload);
+ // First invalidation.
+ tools::Rectangle m_aInvalidation;
+ /// Union of all invalidations.
+ tools::Rectangle m_aInvalidations;
+ Size m_aDocumentSize;
+ OString m_aTextSelection;
+ bool m_bFound;
+ std::vector<OString> m_aSearchResultSelection;
+ std::vector<int> m_aSearchResultPart;
+ int m_nSelectionBeforeSearchResult;
+ int m_nSelectionAfterSearchResult;
+ int m_nInvalidations;
+ int m_nRedlineTableSizeChanged;
+ int m_nRedlineTableEntryModified;
+ int m_nTrackedChangeIndex;
+ bool m_bFullInvalidateSeen;
+ OString m_sHyperlinkText;
+ OString m_sHyperlinkLink;
+ OString m_aFormFieldButton;
+ OString m_aContentControl;
+ OString m_ShapeSelection;
+ TestLokCallbackWrapper m_callbackWrapper;
+};
+
+SwTiledRenderingTest::SwTiledRenderingTest()
+ : SwModelTestBase("/sw/qa/extras/tiledrendering/data/"),
+ m_bFound(true),
+ m_nSelectionBeforeSearchResult(0),
+ m_nSelectionAfterSearchResult(0),
+ m_nInvalidations(0),
+ m_nRedlineTableSizeChanged(0),
+ m_nRedlineTableEntryModified(0),
+ m_nTrackedChangeIndex(-1),
+ m_bFullInvalidateSeen(false),
+ m_callbackWrapper(&callback, this)
+{
+}
+
+void SwTiledRenderingTest::setUp()
+{
+ SwModelTestBase::setUp();
+
+ SwGlobals::ensure();
+ SW_MOD()->ClearRedlineAuthors();
+
+ comphelper::LibreOfficeKit::setActive(true);
+}
+
+void SwTiledRenderingTest::tearDown()
+{
+ if (mxComponent.is())
+ {
+ SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ if (pTextDocument)
+ {
+ SwWrtShell* pWrtShell = pTextDocument->GetDocShell()->GetWrtShell();
+ if (pWrtShell)
+ {
+ pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(nullptr);
+ }
+ }
+ mxComponent->dispose();
+ mxComponent.clear();
+ }
+ m_callbackWrapper.clear();
+ comphelper::LibreOfficeKit::setActive(false);
+
+ test::BootstrapFixture::tearDown();
+}
+
+SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName)
+{
+ if (!pName)
+ createSwDoc();
+ else
+ createSwDoc(pName);
+
+ SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDocument);
+ pTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ return pTextDocument;
+}
+
+void SwTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell)
+{
+ pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell));
+}
+
+void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
+{
+ static_cast<SwTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
+}
+
+void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
+{
+ OString aPayload(pPayload);
+ switch (nType)
+ {
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ {
+ tools::Rectangle aInvalidation;
+ uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
+ if (std::string_view("EMPTY") == pPayload)
+ {
+ m_bFullInvalidateSeen = true;
+ return;
+ }
+
+ CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
+ aInvalidation.SetLeft(aSeq[0].toInt32());
+ aInvalidation.SetTop(aSeq[1].toInt32());
+ aInvalidation.setWidth(aSeq[2].toInt32());
+ aInvalidation.setHeight(aSeq[3].toInt32());
+ if (m_aInvalidation.IsEmpty())
+ {
+ m_aInvalidation = aInvalidation;
+ }
+ m_aInvalidations.Union(aInvalidation);
+ ++m_nInvalidations;
+ }
+ break;
+ case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
+ {
+ uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), aSeq.getLength());
+ m_aDocumentSize.setWidth(aSeq[0].toInt32());
+ m_aDocumentSize.setHeight(aSeq[1].toInt32());
+ }
+ break;
+ case LOK_CALLBACK_TEXT_SELECTION:
+ {
+ m_aTextSelection = pPayload;
+ if (m_aSearchResultSelection.empty())
+ ++m_nSelectionBeforeSearchResult;
+ else
+ ++m_nSelectionAfterSearchResult;
+ }
+ break;
+ case LOK_CALLBACK_SEARCH_NOT_FOUND:
+ {
+ m_bFound = false;
+ }
+ break;
+ case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
+ {
+ m_aSearchResultSelection.clear();
+ boost::property_tree::ptree aTree;
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, aTree);
+ for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("searchResultSelection"))
+ {
+ m_aSearchResultSelection.emplace_back(rValue.second.get<std::string>("rectangles").c_str());
+ m_aSearchResultPart.push_back(std::atoi(rValue.second.get<std::string>("part").c_str()));
+ }
+ }
+ break;
+ case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
+ {
+ ++m_nRedlineTableSizeChanged;
+ }
+ break;
+ case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
+ {
+ ++m_nRedlineTableEntryModified;
+ }
+ break;
+ case LOK_CALLBACK_STATE_CHANGED:
+ {
+ OString aTrackedChangeIndexPrefix(".uno:TrackedChangeIndex="_ostr);
+ if (aPayload.startsWith(aTrackedChangeIndexPrefix))
+ {
+ OString sIndex = aPayload.copy(aTrackedChangeIndexPrefix.getLength());
+ if (sIndex.isEmpty())
+ m_nTrackedChangeIndex = -1;
+ else
+ m_nTrackedChangeIndex = sIndex.toInt32();
+ }
+ }
+ break;
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ {
+ if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ {
+ boost::property_tree::ptree aTree;
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, aTree);
+ boost::property_tree::ptree &aChild = aTree.get_child("hyperlink");
+ m_sHyperlinkText = OString(aChild.get("text", ""));
+ m_sHyperlinkLink = OString(aChild.get("link", ""));
+ }
+ }
+ break;
+ case LOK_CALLBACK_FORM_FIELD_BUTTON:
+ {
+ m_aFormFieldButton = OString(pPayload);
+ }
+ break;
+ case LOK_CALLBACK_CONTENT_CONTROL:
+ {
+ m_aContentControl = OString(pPayload);
+ }
+ break;
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ {
+ m_ShapeSelection = OString(pPayload);
+ }
+ break;
+ }
+
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRegisterCallback)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ // Insert a character at the beginning of the document.
+ pWrtShell->Insert("x");
+ Scheduler::ProcessEventsToIdle();
+
+ // Check that the top left 256x256px tile would be invalidated.
+ CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
+ tools::Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
+ CPPUNIT_ASSERT(m_aInvalidation.Overlaps(aTopLeft));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostKeyEvent)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // Did we manage to go after the first character?
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
+
+ emulateTyping(*pXTextDocument, u"x");
+ // Did we manage to insert the character after the first one?
+ CPPUNIT_ASSERT_EQUAL(OUString("Axaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPostMouseEvent)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // Did we manage to go after the first character?
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCursor->GetPoint()->GetContentIndex());
+
+ Point aStart = pShellCursor->GetSttPos();
+ aStart.setX(aStart.getX() - 1000);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+ // The new cursor position must be before the first word.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCursor->GetPoint()->GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetTextSelection)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Move the cursor into the second word.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
+ // Create a selection on the word.
+ pWrtShell->SelWrd();
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // Did we indeed manage to select the second word?
+ CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
+
+ // Now use setTextSelection() to move the start of the selection 1000 twips left.
+ Point aStart = pShellCursor->GetSttPos();
+ aStart.setX(aStart.getX() - 1000);
+ pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY());
+ // The new selection must include the first word, too -- but not the ending dot.
+ CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb"), pShellCursor->GetText());
+
+ // Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
+ pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
+ pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
+ CPPUNIT_ASSERT_EQUAL(OUString("Aaa b"), pShellCursor->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelection)
+{
+ SwXTextDocument* pXTextDocument = createDoc("shape-with-text.fodt");
+ // No crash, just empty output for unexpected mime type.
+ CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "foo/bar"_ostr));
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Move the cursor into the first word.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
+ // Create a selection by on the word.
+ pWrtShell->SelWrd();
+
+ // Make sure that we selected text from the body text.
+ CPPUNIT_ASSERT_EQUAL("Hello"_ostr, apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr));
+
+ // Make sure we produce something for HTML.
+ CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr).isEmpty());
+
+ // Now select some shape text and check again.
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ pView->SdrBeginTextEdit(pObject);
+ CPPUNIT_ASSERT(pView->GetTextEditObject());
+ EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
+ ESelection aWordSelection(0, 0, 0, 5);
+ rEditView.SetSelection(aWordSelection);
+ CPPUNIT_ASSERT_EQUAL("Shape"_ostr, apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionLineLimit)
+{
+ static OStringLiteral sOriginalText(u8"Estonian employs the Latin script as the basis for its alphabet, which adds the letters ä, ö, ü, and õ, plus the later additions š and ž. The letters c, q, w, x and y are limited to proper names of foreign origin, and f, z, š, and ž appear in loanwords and foreign names only. Ö and Ü are pronounced similarly to their equivalents in Swedish and German. Unlike in standard German but like Swedish (when followed by 'r') and Finnish, Ä is pronounced [æ], as in English mat. The vowels Ä, Ö and Ü are clearly separate phonemes and inherent in Estonian, although the letter shapes come from German. The letter õ denotes /ɤ/, unrounded /o/, or a close-mid back unrounded vowel. It is almost identical to the Bulgarian ъ /ɤ̞/ and the Vietnamese ơ, and is also used to transcribe the Russian ы.");
+ static OStringLiteral sExpectedHtml(u8"Estonian employs the <a href=\"https://en.wikipedia.org/wiki/Latin_script\">Latin script</a> as the basis for <a href=\"https://en.wikipedia.org/wiki/Estonian_alphabet\">its alphabet</a>, which adds the letters <a href=\"https://en.wikipedia.org/wiki/%C3%84\"><i>ä</i></a>, <a href=\"https://en.wikipedia.org/wiki/%C3%96\"><i>ö</i></a>, <a href=\"https://en.wikipedia.org/wiki/%C3%9C\"><i>ü</i></a>, and <a href=\"https://en.wikipedia.org/wiki/%C3%95\"><i>õ</i></a>, plus the later additions <a href=\"https://en.wikipedia.org/wiki/%C5%A0\"><i>š</i></a> and <a href=\"https://en.wikipedia.org/wiki/%C5%BD\"><i>ž</i></a>. The letters <i>c</i>, <i>q</i>, <i>w</i>, <i>x</i> and <i>y</i> are limited to <a href=\"https://en.wikipedia.org/wiki/Proper_names\">proper names</a> of foreign origin, and <i>f</i>, <i>z</i>, <i>š</i>, and <i>ž</i> appear in loanwords and foreign names only. <i>Ö</i> and <i>Ü</i> are pronounced similarly to their equivalents in Swedish and German. Unlike in standard German but like Swedish (when followed by 'r') and Finnish, <i>Ä</i> is pronounced [æ], as in English <i>mat</i>. The vowels Ä, Ö and Ü are clearly separate <a href=\"https://en.wikipedia.org/wiki/Phonemes\">phonemes</a> and inherent in Estonian, although the letter shapes come from German. The letter <a href=\"https://en.wikipedia.org/wiki/%C3%95\"><i>õ</i></a> denotes /ɤ/, unrounded /o/, or a <a href=\"https://en.wikipedia.org/wiki/Close-mid_back_unrounded_vowel\">close-mid back unrounded vowel</a>. It is almost identical to the <a href=\"https://en.wikipedia.org/wiki/Bulgarian_language\">Bulgarian</a> <a href=\"https://en.wikipedia.org/wiki/%D0%AA\">ъ</a> /ɤ̞/ and the <a href=\"https://en.wikipedia.org/wiki/Vietnamese_language\">Vietnamese</a> <a href=\"https://en.wikipedia.org/wiki/%C6%A0\">ơ</a>, and is also used to transcribe the Russian <a href=\"https://en.wikipedia.org/wiki/%D0%AB\">ы</a>.");
+
+ SwXTextDocument* pXTextDocument = createDoc("estonian.odt");
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Move the cursor into the first word.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
+ // Create a selection.
+ pWrtShell->SelAll();
+
+ OString sPlainText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr);
+
+ CPPUNIT_ASSERT_EQUAL(OString(sOriginalText), sPlainText.trim());
+
+ OString sHtmlText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr);
+
+ int nStart = sHtmlText.indexOf(u8"Estonian");
+
+ CPPUNIT_ASSERT(sHtmlText.match(sExpectedHtml, nStart));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetTextSelectionMultiLine)
+{
+ // Test will check if correct number of new line marks / paragraphs is generated
+ static OStringLiteral sOriginalText(u8"Heading\n\
+Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:\n\
+\n\
+\n\
+\n\
+\n\
+\n\
+And this is all for Writer shape objects\n\
+Heading on second page");
+
+ static OStringLiteral sExpectedHtml(u8"Heading</h2>\n\
+<p>Let's have text; we need to be able to select the text inside the shape, but also the various individual ones too:</p>\n\
+<p><br/><br/></p>\n\
+<p><br/><br/></p>\n\
+<p><br/><br/></p>\n\
+<p><br/><br/></p>\n\
+<p><br/><br/></p>\n\
+<h1 class=\"western\">And this is all for Writer shape objects</h1>\n\
+<h2 class=\"western\">Heading on second page</h2>");
+
+ SwXTextDocument* pXTextDocument = createDoc("multiline.odt");
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Create a selection.
+ pWrtShell->SelAll();
+
+ OString sPlainText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"_ostr);
+
+ CPPUNIT_ASSERT_EQUAL(OString(sOriginalText), sPlainText.trim());
+
+ OString sHtmlText = apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html"_ostr);
+
+ int nStart = sHtmlText.indexOf(u8"Heading");
+
+ CPPUNIT_ASSERT(sHtmlText.match(sExpectedHtml, nStart));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetGraphicSelection)
+{
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ pWrtShell->SelectObj(Point(), 0, pObject);
+ SdrHdlList handleList(nullptr);
+ pObject->AddToHdlList(handleList);
+ // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8), handleList.GetHdlCount());
+ // Take the bottom center one.
+ SdrHdl* pHdl = handleList.GetHdl(6);
+ CPPUNIT_ASSERT_EQUAL(int(SdrHdlKind::Lower), static_cast<int>(pHdl->GetKind()));
+ tools::Rectangle aShapeBefore = pObject->GetSnapRect();
+ // Resize.
+ pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
+ pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
+ tools::Rectangle aShapeAfter = pObject->GetSnapRect();
+ // Check that a resize happened, but aspect ratio is not kept.
+ CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenWidth(), aShapeAfter.getOpenWidth());
+ CPPUNIT_ASSERT_EQUAL(aShapeBefore.getOpenHeight() + 1000, aShapeAfter.getOpenHeight());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testResetSelection)
+{
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Select one character.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false);
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // We have a text selection.
+ CPPUNIT_ASSERT(pShellCursor->HasMark());
+
+ pXTextDocument->resetSelection();
+ // We no longer have a text selection.
+ CPPUNIT_ASSERT(!pShellCursor->HasMark());
+
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ Point aPoint = pObject->GetSnapRect().Center();
+ // Select the shape.
+ pWrtShell->EnterSelFrameMode(&aPoint);
+ // We have a graphic selection.
+ CPPUNIT_ASSERT(pWrtShell->IsSelFrameMode());
+
+ pXTextDocument->resetSelection();
+ // We no longer have a graphic selection.
+ CPPUNIT_ASSERT(!pWrtShell->IsSelFrameMode());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testInsertShape)
+{
+ SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+
+ pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
+ comphelper::dispatchCommand(".uno:BasicShapes.circle", uno::Sequence<beans::PropertyValue>());
+
+ // check that the shape was inserted in the visible area, not outside
+ IDocumentDrawModelAccess &rDrawModelAccess = pWrtShell->GetDoc()->getIDocumentDrawModelAccess();
+ SdrPage* pPage = rDrawModelAccess.GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+
+ CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(3299, 299), Size(3403, 3403)), pObject->GetSnapRect());
+
+ // check that it is in the foreground layer
+ CPPUNIT_ASSERT_EQUAL(rDrawModelAccess.GetHeavenId().get(), pObject->GetLayer().get());
+}
+
+static void lcl_search(bool bBackward)
+{
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::Any(OUString("shape"))},
+ {"SearchItem.Backward", uno::Any(bBackward)}
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearch)
+{
+ SwXTextDocument* pXTextDocument = createDoc("search.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ SwNodeOffset nNode = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
+
+ // First hit, in the second paragraph, before the shape.
+ lcl_search(false);
+ CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
+ SwNodeOffset nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
+ CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
+ /// Make sure we get search result selection for normal find as well, not only find all.
+ CPPUNIT_ASSERT(!m_aSearchResultSelection.empty());
+
+ // Next hit, in the shape.
+ lcl_search(false);
+ CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
+
+ // Next hit, in the shape, still.
+ lcl_search(false);
+ CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
+
+ // Last hit, in the last paragraph, after the shape.
+ lcl_search(false);
+ CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
+ nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
+ CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
+
+ // Now change direction and make sure that the first 2 hits are in the shape, but not the 3rd one.
+ lcl_search(true);
+ CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
+ lcl_search(true);
+ CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());
+ lcl_search(true);
+ CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
+ nActual = pWrtShell->getShellCursor(false)->Start()->GetNode().GetIndex();
+ CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchViewArea)
+{
+ SwXTextDocument* pXTextDocument = createDoc("search.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Go to the second page, 1-based.
+ pWrtShell->GotoPage(2, false);
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // Get the ~top left corner of the second page.
+ Point aPoint = pShellCursor->GetSttPos();
+
+ // Go back to the first page, search while the cursor is there, but the
+ // visible area is the second page.
+ pWrtShell->GotoPage(1, false);
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::Any(OUString("Heading"))},
+ {"SearchItem.Backward", uno::Any(false)},
+ {"SearchItem.SearchStartPointX", uno::Any(static_cast<sal_Int32>(aPoint.getX()))},
+ {"SearchItem.SearchStartPointY", uno::Any(static_cast<sal_Int32>(aPoint.getY()))}
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ // This was just "Heading", i.e. SwView::SearchAndWrap() did not search from only the top of the second page.
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading on second page"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrame)
+{
+ SwXTextDocument* pXTextDocument = createDoc("search.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::Any(OUString("TextFrame"))},
+ {"SearchItem.Backward", uno::Any(false)},
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ // This was empty: nothing was highlighted after searching for 'TextFrame'.
+ CPPUNIT_ASSERT(!m_aTextSelection.isEmpty());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchTextFrameWrapAround)
+{
+ SwXTextDocument* pXTextDocument = createDoc("search.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::Any(OUString("TextFrame"))},
+ {"SearchItem.Backward", uno::Any(false)},
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ CPPUNIT_ASSERT(m_bFound);
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ // This failed, i.e. the second time 'not found' was reported, instead of wrapping around.
+ CPPUNIT_ASSERT(m_bFound);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentSizeChanged)
+{
+ // Get the current document size.
+ SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ Size aSize = pXTextDocument->getDocumentSize();
+
+ // Delete the second page and see how the size changes.
+ pWrtShell->Down(false);
+ pWrtShell->DelLeft();
+ // Document width should not change, this was 0.
+ CPPUNIT_ASSERT_EQUAL(aSize.getWidth(), m_aDocumentSize.getWidth());
+ // Document height should be smaller now.
+ CPPUNIT_ASSERT(aSize.getHeight() > m_aDocumentSize.getHeight());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAll)
+{
+ SwXTextDocument* pXTextDocument = createDoc("search.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::Any(OUString("shape"))},
+ {"SearchItem.Backward", uno::Any(false)},
+ {"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ // This was 0; should be 2 results in the body text.
+ CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), m_aSearchResultSelection.size());
+ // Writer documents are always a single part.
+ CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart[0]);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSearchAllNotifications)
+{
+ SwXTextDocument* pXTextDocument = createDoc("search.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ // Reset notification counter before search.
+ m_nSelectionBeforeSearchResult = 0;
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::Any(OUString("shape"))},
+ {"SearchItem.Backward", uno::Any(false)},
+ {"SearchItem.Command", uno::Any(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+
+ // This was 5, make sure that we get no notifications about selection changes during search.
+ CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult);
+ // But we do get the selection afterwards.
+ CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageDownInvalidation)
+{
+ SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {".uno:HideWhitespace", uno::Any(true)},
+ }));
+ pXTextDocument->initializeForTiledRendering(aPropertyValues);
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ comphelper::dispatchCommand(".uno:PageDown", uno::Sequence<beans::PropertyValue>());
+
+ // This was 2.
+ CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPartHash)
+{
+ SwXTextDocument* pXTextDocument = createDoc("pagedown-invalidation.odt");
+ int nParts = pXTextDocument->getParts();
+ for (int it = 0; it < nParts; it++)
+ {
+ CPPUNIT_ASSERT(!pXTextDocument->getPartHash(it).isEmpty());
+ }
+}
+
+namespace {
+
+ /// A view callback tracks callbacks invoked on one specific view.
+ class ViewCallback final
+ {
+ SfxViewShell* mpViewShell;
+ int mnView;
+ public:
+ bool m_bOwnCursorInvalidated;
+ int m_nOwnCursorInvalidatedBy;
+ bool m_bOwnCursorAtOrigin;
+ tools::Rectangle m_aOwnCursor;
+ bool m_bViewCursorInvalidated;
+ tools::Rectangle m_aViewCursor;
+ bool m_bOwnSelectionSet;
+ bool m_bViewSelectionSet;
+ OString m_aViewSelection;
+ bool m_bTilesInvalidated;
+ bool m_bViewCursorVisible;
+ bool m_bGraphicViewSelection;
+ bool m_bGraphicSelection;
+ bool m_bViewLock;
+ /// Set if any callback was invoked.
+ bool m_bCalled;
+ /// Redline table size changed payload
+ boost::property_tree::ptree m_aRedlineTableChanged;
+ /// Redline table modified payload
+ boost::property_tree::ptree m_aRedlineTableModified;
+ /// Post-it / annotation payload.
+ boost::property_tree::ptree m_aComment;
+ TestLokCallbackWrapper m_callbackWrapper;
+
+ ViewCallback(SfxViewShell* pViewShell = nullptr, std::function<void(ViewCallback&)> const & rBeforeInstallFunc = {})
+ : m_bOwnCursorInvalidated(false),
+ m_nOwnCursorInvalidatedBy(-1),
+ m_bOwnCursorAtOrigin(false),
+ m_bViewCursorInvalidated(false),
+ m_bOwnSelectionSet(false),
+ m_bViewSelectionSet(false),
+ m_bTilesInvalidated(false),
+ m_bViewCursorVisible(false),
+ m_bGraphicViewSelection(false),
+ m_bGraphicSelection(false),
+ m_bViewLock(false),
+ m_bCalled(false),
+ m_callbackWrapper(&callback, this)
+ {
+ // Because one call-site wants to set the bool fields up before the callback is installed
+ if (rBeforeInstallFunc)
+ rBeforeInstallFunc(*this);
+
+ mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current();
+ mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ mnView = SfxLokHelper::getView();
+ m_callbackWrapper.setLOKViewId( mnView );
+ }
+
+ ~ViewCallback()
+ {
+ SfxLokHelper::setView(mnView);
+ mpViewShell->setLibreOfficeKitViewCallback(nullptr);
+ }
+
+ static void callback(int nType, const char* pPayload, void* pData)
+ {
+ static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
+ }
+
+ void callbackImpl(int nType, const char* pPayload)
+ {
+ OString aPayload(pPayload);
+ m_bCalled = true;
+ switch (nType)
+ {
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ {
+ m_bTilesInvalidated = true;
+ }
+ break;
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ {
+ m_bOwnCursorInvalidated = true;
+
+ OString sRect;
+ if(comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ {
+ std::stringstream aStream(pPayload);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ sRect = OString(aTree.get_child("rectangle").get_value<std::string>());
+ m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value<int>();
+ }
+ else
+ sRect = aPayload;
+ uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect));
+ if (std::string_view("EMPTY") == pPayload)
+ return;
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
+ m_aOwnCursor.SetLeft(aSeq[0].toInt32());
+ m_aOwnCursor.SetTop(aSeq[1].toInt32());
+ m_aOwnCursor.setWidth(aSeq[2].toInt32());
+ m_aOwnCursor.setHeight(aSeq[3].toInt32());
+ if (m_aOwnCursor.Left() == 0 && m_aOwnCursor.Top() == 0)
+ m_bOwnCursorAtOrigin = true;
+ }
+ break;
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ {
+ m_bViewCursorInvalidated = true;
+ std::stringstream aStream(pPayload);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString aRect( aTree.get_child("rectangle").get_value<std::string>() );
+
+ uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect));
+ if (std::string_view("EMPTY") == pPayload)
+ return;
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
+ m_aViewCursor.SetLeft(aSeq[0].toInt32());
+ m_aViewCursor.SetTop(aSeq[1].toInt32());
+ m_aViewCursor.setWidth(aSeq[2].toInt32());
+ m_aViewCursor.setHeight(aSeq[3].toInt32());
+ }
+ break;
+ case LOK_CALLBACK_TEXT_SELECTION:
+ {
+ m_bOwnSelectionSet = true;
+ }
+ break;
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ {
+ m_bViewSelectionSet = true;
+ m_aViewSelection = aPayload;
+ }
+ break;
+ case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
+ {
+ std::stringstream aStream(pPayload);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ m_bViewCursorVisible = aTree.get_child("visible").get_value<std::string>() == "true";
+ }
+ break;
+ case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
+ {
+ std::stringstream aStream(pPayload);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ m_bGraphicViewSelection = aTree.get_child("selection").get_value<std::string>() != "EMPTY";
+ }
+ break;
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ {
+ m_bGraphicSelection = aPayload != "EMPTY";
+ }
+ break;
+ case LOK_CALLBACK_VIEW_LOCK:
+ {
+ std::stringstream aStream(pPayload);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ m_bViewLock = aTree.get_child("rectangle").get_value<std::string>() != "EMPTY";
+ }
+ break;
+ case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
+ {
+ m_aRedlineTableChanged.clear();
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, m_aRedlineTableChanged);
+ m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline");
+ }
+ break;
+ case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
+ {
+ m_aRedlineTableModified.clear();
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, m_aRedlineTableModified);
+ m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline");
+ }
+ break;
+ case LOK_CALLBACK_COMMENT:
+ {
+ m_aComment.clear();
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, m_aComment);
+ m_aComment = m_aComment.get_child("comment");
+ }
+ break;
+ }
+ }
+ };
+
+ class TestResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
+ {
+ public:
+ sal_uInt32 m_nDocRepair;
+
+ TestResultListener() : m_nDocRepair(0)
+ {
+ }
+
+ virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
+ {
+ if (rEvent.State == frame::DispatchResultState::SUCCESS)
+ {
+ rEvent.Result >>= m_nDocRepair;
+ }
+ }
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject&) override
+ {
+ }
+ };
+
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation)
+{
+ // Create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ ViewCallback aView1;
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ ViewCallback aView2;
+ int nView2 = SfxLokHelper::getView();
+
+ // First view: put the cursor into the first word.
+ SfxLokHelper::setView(nView1);
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+
+ // Second view: select the first word.
+ SfxLokHelper::setView(nView2);
+ CPPUNIT_ASSERT(pXTextDocument->GetDocShell()->GetWrtShell() != pWrtShell);
+ pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ pWrtShell->SelWrd();
+
+ // Now delete the selected word and make sure both views are invalidated.
+ Scheduler::ProcessEventsToIdle();
+ aView1.m_bTilesInvalidated = false;
+ aView2.m_bTilesInvalidated = false;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
+ CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursors)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ ViewCallback aView1;
+ SfxLokHelper::createView();
+ ViewCallback aView2;
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
+ CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
+ // This failed: the cursor position of view1 was only known to view2 once
+ // it changed.
+ CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
+
+ // Make sure that aView1 gets a view-only selection notification, while
+ // aView2 gets a real selection notification.
+ aView1.m_bOwnSelectionSet = false;
+ aView1.m_bViewSelectionSet = false;
+ aView2.m_bOwnSelectionSet = false;
+ aView2.m_bViewSelectionSet = false;
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Move the cursor into the second word.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
+ // Create a selection on the word.
+ pWrtShell->SelWrd();
+ Scheduler::ProcessEventsToIdle();
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // Did we indeed manage to select the second word?
+ CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
+ CPPUNIT_ASSERT(!aView1.m_bOwnSelectionSet);
+ // This failed, aView1 did not get notification about selection changes in
+ // aView2.
+ CPPUNIT_ASSERT(aView1.m_bViewSelectionSet);
+ CPPUNIT_ASSERT(aView2.m_bOwnSelectionSet);
+ CPPUNIT_ASSERT(!aView2.m_bViewSelectionSet);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeViewCursors)
+{
+ // Load a document and create a view, so we have 2 ones.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ ViewCallback aView1;
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ ViewCallback aView2;
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+
+ // Start shape text in the second view.
+ SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell2->GetDrawView();
+ pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
+ emulateTyping(*pXTextDocument, u"x");
+ // Press a key in the second view, while the first one observes this.
+ aView1.m_bViewCursorInvalidated = false;
+ aView2.m_bOwnCursorInvalidated = false;
+ const tools::Rectangle aLastOwnCursor1 = aView1.m_aOwnCursor;
+ const tools::Rectangle aLastViewCursor1 = aView1.m_aViewCursor;
+ const tools::Rectangle aLastOwnCursor2 = aView2.m_aOwnCursor;
+ const tools::Rectangle aLastViewCursor2 = aView2.m_aViewCursor;
+
+ emulateTyping(*pXTextDocument, u"y");
+ // Make sure that aView1 gets a view-only cursor notification, while
+ // aView2 gets a real cursor notification.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aLastOwnCursor1);
+ CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aLastViewCursor1 != aView1.m_aViewCursor);
+ CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
+ CPPUNIT_ASSERT(aLastOwnCursor2 != aView2.m_aOwnCursor);
+ CPPUNIT_ASSERT_EQUAL(aLastViewCursor2, aView2.m_aViewCursor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorVisibility)
+{
+ // Load a document that has a shape and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ ViewCallback aView1;
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ ViewCallback aView2;
+ // This failed, initially the view cursor in the second view wasn't visible.
+ CPPUNIT_ASSERT(aView2.m_bViewCursorVisible);
+
+ // Click on the shape in the second view.
+ aView1.m_bViewCursorVisible = true;
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ Point aCenter = pObject->GetSnapRect().Center();
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+ // Make sure the "view/text" cursor of the first view gets a notification.
+ CPPUNIT_ASSERT(!aView1.m_bViewCursorVisible);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewCursorCleanup)
+{
+ // Load a document that has a shape and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ ViewCallback aView1;
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ {
+ ViewCallback aView2;
+
+ // Click on the shape in the second view.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ Point aCenter = pObject->GetSnapRect().Center();
+ aView1.m_bGraphicViewSelection = false;
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aCenter.getX(), aCenter.getY(), 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+ // Make sure there is a graphic view selection on the first view.
+ CPPUNIT_ASSERT(aView1.m_bGraphicViewSelection);
+ }
+ // Now destroy the second view.
+ SfxLokHelper::destroyView(nView2);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), SfxLokHelper::getViewsCount(0));
+ // Make sure that the graphic view selection on the first view is cleaned up.
+ CPPUNIT_ASSERT(!aView1.m_bGraphicViewSelection);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testViewLock)
+{
+ // Load a document that has a shape and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ ViewCallback aView1;
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ ViewCallback aView2;
+
+ // Begin text edit in the second view and assert that the first gets a lock
+ // notification.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ aView1.m_bViewLock = false;
+ pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
+ CPPUNIT_ASSERT(aView1.m_bViewLock);
+
+ // End text edit in the second view, and assert that the lock is removed in
+ // the first view.
+ pWrtShell->EndTextEdit();
+ CPPUNIT_ASSERT(!aView1.m_bViewLock);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTextEditViewInvalidations)
+{
+ // Load a document that has a shape and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ ViewCallback aView1;
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ ViewCallback aView2;
+
+ // Begin text edit in the second view.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
+ emulateTyping(*pXTextDocument, u"x");
+
+ // Assert that both views are invalidated when pressing a key while in text edit.
+ aView1.m_bTilesInvalidated = false;
+ emulateTyping(*pXTextDocument, u"y");
+
+ CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
+
+ pWrtShell->EndTextEdit();
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoInvalidations)
+{
+ // Load a document and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ ViewCallback aView1;
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ ViewCallback aView2;
+ SfxLokHelper::setView(nView1);
+
+ // Insert a character the end of the document.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->EndOfSection();
+ emulateTyping(*pXTextDocument, u"c");
+ // ProcessEventsToIdle resets the view; set it again
+ SfxLokHelper::setView(nView1);
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb.c"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+
+ // Undo and assert that both views are invalidated.
+ Scheduler::ProcessEventsToIdle();
+ aView1.m_bTilesInvalidated = false;
+ aView2.m_bTilesInvalidated = false;
+ comphelper::dispatchCommand(".uno:Undo", {});
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
+ // Undo was dispatched on the first view, this second view was not invalidated.
+ CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoLimiting)
+{
+ // Load a document and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+
+ // Insert a character the end of the document in the second view.
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell2->EndOfSection();
+ emulateTyping(*pXTextDocument, u"c");
+ SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb.c"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+
+ // Assert that the first view can't undo, but the second view can.
+ CPPUNIT_ASSERT(!pWrtShell1->GetLastUndoInfo(nullptr, nullptr, &pWrtShell1->GetView()));
+ CPPUNIT_ASSERT(pWrtShell2->GetLastUndoInfo(nullptr, nullptr, &pWrtShell2->GetView()));
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReordering)
+{
+ // Create two views and a document of 2 paragraphs.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell2->SplitNode();
+ SfxLokHelper::setView(nView1);
+ pWrtShell1->SttEndDoc(/*bStt=*/true);
+ SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
+ // View 1 types into the first paragraph.
+ emulateTyping(*pXTextDocument, u"a");
+ SfxLokHelper::setView(nView2);
+ pWrtShell2->SttEndDoc(/*bStt=*/false);
+ SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
+ // View 2 types into the second paragraph.
+ emulateTyping(*pXTextDocument, u"z");
+ CPPUNIT_ASSERT_EQUAL(OUString("a"), pTextNode1->GetText());
+ CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
+
+ // When view 1 presses undo:
+ SfxLokHelper::setView(nView1);
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ // Then make sure view 1's last undo action is invoked, out of order:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expression: pTextNode1->GetText().isEmpty()
+ // i.e. the "a" in the first paragraph was not removed.
+ CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
+ // Last undo action is not invoked, as it belongs to view 2.
+ CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo)
+{
+ // Create two views and a document of 2 paragraphs.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell2->SplitNode();
+ SfxLokHelper::setView(nView1);
+ pWrtShell1->SttEndDoc(/*bStt=*/true);
+ SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
+ // View 1 types into the first paragraph, twice.
+ emulateTyping(*pXTextDocument, u"f");
+ // Go to the start of the paragraph, to avoid grouping.
+ pWrtShell1->SttEndDoc(/*bStt=*/true);
+ emulateTyping(*pXTextDocument, u"s");
+ SfxLokHelper::setView(nView2);
+ pWrtShell2->SttEndDoc(/*bStt=*/false);
+ SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
+ // View 2 types into the second paragraph.
+ emulateTyping(*pXTextDocument, u"z");
+ CPPUNIT_ASSERT_EQUAL(OUString("sf"), pTextNode1->GetText());
+ CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
+
+ // When view 1 presses undo, twice:
+ SfxLokHelper::setView(nView1);
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+ // First just s(econd) is erased:
+ CPPUNIT_ASSERT_EQUAL(OUString("f"), pTextNode1->GetText());
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ // Then make sure view 1's undo actions are invoked, out of order:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expression: pTextNode1->GetText().isEmpty()
+ // i.e. out of order undo was executed only once, not twice.
+ CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
+ // The top undo action is not invoked, as it belongs to view 2.
+ CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingRedo2)
+{
+ // Create two views.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+
+ // Type in the first view.
+ SfxLokHelper::setView(nView1);
+ pWrtShell1->SttEndDoc(/*bStt=*/true);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'f', 0);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'f', 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Type to the same paragraph in the second view.
+ SfxLokHelper::setView(nView2);
+ pWrtShell2->SttEndDoc(/*bStt=*/true);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 's', 0);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 's', 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Delete in the first view and undo.
+ SfxLokHelper::setView(nView1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
+ Scheduler::ProcessEventsToIdle();
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+ Scheduler::ProcessEventsToIdle();
+
+ // Query the undo state, now that a "delete" is on the redo stack and an "insert" belongs to the
+ // view on the undo stack, so the types are different.
+ SwUndoId nUndoId(SwUndoId::EMPTY);
+ // Without the accompanying fix in place, this test would have failed with:
+ // runtime error: downcast which does not point to an object of type 'const SwUndoInsert'
+ // note: object is of type 'SwUndoDelete'
+ // in an UBSan build.
+ pWrtShell1->GetLastUndoInfo(nullptr, &nUndoId, &pWrtShell1->GetView());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoReorderingMulti)
+{
+ // Create two views and a document of 2 paragraphs.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell2->SplitNode();
+ SfxLokHelper::setView(nView1);
+ pWrtShell1->SttEndDoc(/*bStt=*/true);
+ SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetPointNode().GetTextNode();
+ // View 1 types into the first paragraph.
+ emulateTyping(*pXTextDocument, u"a");
+ SfxLokHelper::setView(nView2);
+ pWrtShell2->SttEndDoc(/*bStt=*/false);
+ SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetPointNode().GetTextNode();
+ // View 2 types into the second paragraph, twice.
+ emulateTyping(*pXTextDocument, u"x");
+ // Go to the start of the paragraph, to avoid grouping.
+ pWrtShell2->SttPara();
+ emulateTyping(*pXTextDocument, u"y");
+ CPPUNIT_ASSERT_EQUAL(OUString("a"), pTextNode1->GetText());
+ CPPUNIT_ASSERT_EQUAL(OUString("yx"), pTextNode2->GetText());
+
+ // When view 1 presses undo:
+ SfxLokHelper::setView(nView1);
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ // Then make sure view 1's undo action is invoked, out of order:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expression: pTextNode1->GetText().isEmpty()
+ // i.e. out of order undo was not executed, the first paragraph was still "a".
+ CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
+ // The top 2 undo actions are not invoked, as they belong to view 2.
+ CPPUNIT_ASSERT_EQUAL(OUString("yx"), pTextNode2->GetText());
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoShapeLimiting)
+{
+ // Load a document and create a view.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
+
+ // Start shape text in the second view.
+ SdrPage* pPage = pWrtShell2->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell2->GetDrawView();
+ pWrtShell2->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell2->GetWin());
+ emulateTyping(*pXTextDocument, u"x");
+ pWrtShell2->EndTextEdit();
+
+ // Assert that the first view can't and the second view can undo the insertion.
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+ rUndoManager.SetView(&pWrtShell1->GetView());
+ // This was 1: first view could undo the change of the second view.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
+ rUndoManager.SetView(&pWrtShell2->GetView());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
+
+ rUndoManager.SetView(nullptr);
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoDispatch)
+{
+ // Load a document and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ int nView2 = SfxLokHelper::getView();
+
+ // Insert a character in the first view.
+ SfxLokHelper::setView(nView1);
+ emulateTyping(*pXTextDocument, u"c");
+
+ // Click before the first word in the second view.
+ SfxLokHelper::setView(nView2);
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ Point aStart = pShellCursor->GetSttPos();
+ aStart.setX(aStart.getX() - 1000);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
+ uno::Reference<frame::XFrame> xFrame2 = xDesktop->getActiveFrame();
+
+ // Now switch back to the first view, and make sure that the active frame is updated.
+ SfxLokHelper::setView(nView1);
+ uno::Reference<frame::XFrame> xFrame1 = xDesktop->getActiveFrame();
+ // This failed: setView() did not update the active frame.
+ CPPUNIT_ASSERT(xFrame1 != xFrame2);
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairDispatch)
+{
+ // Load a document and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ int nView2 = SfxLokHelper::getView();
+
+ // Insert a character in the first view.
+ SfxLokHelper::setView(nView1);
+ emulateTyping(*pXTextDocument, u"c");
+
+ // Assert that by default the second view can't undo the action.
+ SfxLokHelper::setView(nView2);
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
+ comphelper::dispatchCommand(".uno:Undo", {});
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
+
+ // But the same is allowed in repair mode.
+ SfxLokHelper::setView(nView2);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rUndoManager.GetUndoActionCount());
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"Repair", uno::Any(true)}
+ }));
+ comphelper::dispatchCommand(".uno:Undo", aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+ // This was 1: repair mode couldn't undo the action, either.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rUndoManager.GetUndoActionCount());
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoShells)
+{
+ // Load a document and create a view.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ sal_Int32 nView1 = SfxLokHelper::getView();
+
+ // Begin text edit.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
+ emulateTyping(*pXTextDocument, u"x");
+ pWrtShell->EndTextEdit();
+
+ // Make sure that the undo item remembers who created it.
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
+ CPPUNIT_ASSERT_EQUAL(OUString("Edit text of Shape 'Shape1'"), rUndoManager.GetUndoActionComment(0));
+
+ // This was -1: the view shell id for the undo action wasn't known.
+ CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeTextUndoGroupShells)
+{
+ // Load a document and create a view.
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ ViewCallback aView1;
+ sal_Int32 nView1 = SfxLokHelper::getView();
+
+ // Begin text edit.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
+ emulateTyping(*pXTextDocument, u"x");
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::BACKSPACE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::BACKSPACE);
+ Scheduler::ProcessEventsToIdle();
+
+ // Make sure that the undo item remembers who created it.
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rUndoManager.GetUndoActionCount());
+
+ pWrtShell->EndTextEdit();
+ pWrtShell->GetView().BeginTextEdit(pObject, pView->GetSdrPageView(), pWrtShell->GetWin());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rUndoManager.GetUndoActionCount());
+ CPPUNIT_ASSERT_EQUAL(OUString("Edit text of Shape 'Shape1'"), rUndoManager.GetUndoActionComment(0));
+
+ // This was -1: the view shell id for the (top) undo list action wasn't known.
+ CPPUNIT_ASSERT_EQUAL(ViewShellId(nView1), rUndoManager.GetUndoAction()->GetViewShellId());
+
+ // Create an editeng text selection in the first view.
+ EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
+ emulateTyping(*pXTextDocument, u"x");
+ // 0th para, 0th char -> 0th para, 1st char.
+ ESelection aWordSelection(0, 0, 0, 1);
+ rEditView.SetSelection(aWordSelection);
+
+ // Create a second view, and make sure that the new view sees the same
+ // cursor position as the old one.
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering({});
+ ViewCallback aView2;
+ // Difference was 935 twips, the new view didn't see the editeng cursor of
+ // the old one. The new difference should be <1px, but here we deal with twips.
+ CPPUNIT_ASSERT(std::abs(aView1.m_aOwnCursor.Top() - aView2.m_aViewCursor.Top()) < 10);
+ // This was false, editeng text selection of the first view wasn't noticed
+ // by the second view.
+ CPPUNIT_ASSERT(aView2.m_bViewSelectionSet);
+ // This was false, the new view wasn't aware of the shape text lock created
+ // by the old view.
+ CPPUNIT_ASSERT(aView2.m_bViewLock);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChanges)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+
+ // Turn on track changes, type "zzz" at the end, and move to the start.
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ ViewCallback aView(pWrtShell->GetSfxViewShell());
+ pWrtShell->EndOfSection();
+ pWrtShell->Insert("zzz");
+ pWrtShell->StartOfSection();
+
+ // Get the redline just created
+ const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
+ SwRangeRedline* pRedline = rTable[0];
+
+ // Reject the change by id, while the cursor does not cover the tracked change.
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"RejectTrackedChange", uno::Any(o3tl::narrowing<sal_uInt16>(pRedline->GetId()))}
+ }));
+ comphelper::dispatchCommand(".uno:RejectTrackedChange", aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+
+ // Assert that the reject was performed.
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // This was 'Aaa bbb.zzz', the change wasn't rejected.
+ CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTrackChangesCallback)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Turn on track changes and type "x".
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
+ m_nRedlineTableSizeChanged = 0;
+ pWrtShell->Insert("x");
+
+ // Assert that we get exactly one notification about the redline insert.
+ // This was 0, as LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED wasn't sent.
+ CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableSizeChanged);
+
+ CPPUNIT_ASSERT_EQUAL(-1, m_nTrackedChangeIndex);
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ SfxItemSet aSet(pWrtShell->GetDoc()->GetAttrPool(), svl::Items<FN_REDLINE_ACCEPT_DIRECT, FN_REDLINE_ACCEPT_DIRECT>);
+ SfxVoidItem aItem(FN_REDLINE_ACCEPT_DIRECT);
+ aSet.Put(aItem);
+ pWrtShell->GetView().GetState(aSet);
+ // This failed, LOK_CALLBACK_STATE_CHANGED wasn't sent.
+ CPPUNIT_ASSERT_EQUAL(0, m_nTrackedChangeIndex);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineUpdateCallback)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Turn on track changes, type "xx" and delete the second one.
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
+ pWrtShell->Insert("xx");
+ m_nRedlineTableEntryModified = 0;
+ pWrtShell->DelLeft();
+
+ // Assert that we get exactly one notification about the redline update.
+ // This was 0, as LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED wasn't sent.
+ CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
+
+ // Turn off the change tracking mode, make some modification to left of the
+ // redline so that its position changes
+ xPropertySet->setPropertyValue("RecordChanges", uno::Any(false));
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ pWrtShell->Insert("This text is left of the redline");
+
+ // Position of the redline has changed => Modify callback
+ CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
+
+ pWrtShell->DelLeft();
+ // Deletion also emits Modify callback
+ CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
+
+ // Make changes to the right of the redline => no position change in redline
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 100/*Go enough right */, /*bBasicCall=*/false);
+ pWrtShell->Insert("This text is right of the redline");
+
+ // No Modify callbacks
+ CPPUNIT_ASSERT_EQUAL(3, m_nRedlineTableEntryModified);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testGetViewRenderState)
+{
+ SwXTextDocument* pXTextDocument = createDoc();
+ int nFirstViewId = SfxLokHelper::getView();
+ ViewCallback aView1;
+ {
+ SwViewOption aViewOptions;
+ aViewOptions.SetViewMetaChars(true);
+ aViewOptions.SetOnlineSpell(true);
+ pXTextDocument->GetDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
+ }
+ CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
+
+ // Create a second view
+ SfxLokHelper::createView();
+ int nSecondViewId = SfxLokHelper::getView();
+ ViewCallback aView2;
+ {
+ // Give the second view different options
+ SwViewOption aViewOptions;
+ aViewOptions.SetViewMetaChars(false);
+ aViewOptions.SetOnlineSpell(true);
+ pXTextDocument->GetDocShell()->GetWrtShell()->ApplyViewOptions(aViewOptions);
+ }
+ CPPUNIT_ASSERT_EQUAL("S;Default"_ostr, pXTextDocument->getViewRenderState());
+
+ // Switch back to the first view, and check that the options string is the same
+ SfxLokHelper::setView(nFirstViewId);
+ CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
+
+ // Switch back to the second view, and change to dark mode
+ SfxLokHelper::setView(nSecondViewId);
+ {
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ SwView* pView = pDoc->GetDocShell()->GetView();
+ uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
+ uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
+ {
+ { "NewTheme", uno::Any(OUString("Dark")) },
+ }
+ );
+ comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
+ }
+ CPPUNIT_ASSERT_EQUAL("S;Dark"_ostr, pXTextDocument->getViewRenderState());
+ // Switch back to the first view, and check that the options string is the same
+ SfxLokHelper::setView(nFirstViewId);
+ CPPUNIT_ASSERT_EQUAL("PS;Default"_ostr, pXTextDocument->getViewRenderState());
+}
+
+// Helper function to get a tile to a bitmap and check the pixel color
+static void assertTilePixelColor(SwXTextDocument* pXTextDocument, int nPixelX, int nPixelY, Color aColor)
+{
+ size_t nCanvasSize = 1024;
+ size_t nTileSize = 256;
+ std::vector<unsigned char> aPixmap(nCanvasSize * nCanvasSize * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasSize, nCanvasSize),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasSize, nCanvasSize, 0, 0, 15360, 7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+ Color aActualColor(pAccess->GetPixel(nPixelX, nPixelY));
+ CPPUNIT_ASSERT_EQUAL(aColor, aActualColor);
+}
+
+// Test that changing the theme in one view doesn't change it in the other view
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testThemeViewSeparation)
+{
+ Color aDarkColor(0x1c, 0x1c, 0x1c);
+ // Add a minimal dark scheme
+ {
+ svtools::EditableColorConfig aColorConfig;
+ svtools::ColorConfigValue aValue;
+ aValue.bIsVisible = true;
+ aValue.nColor = aDarkColor;
+ aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
+ aColorConfig.AddScheme(u"Dark"_ustr);
+ }
+ // Add a minimal light scheme
+ {
+ svtools::EditableColorConfig aColorConfig;
+ svtools::ColorConfigValue aValue;
+ aValue.bIsVisible = true;
+ aValue.nColor = COL_WHITE;
+ aColorConfig.SetColorValue(svtools::DOCCOLOR, aValue);
+ aColorConfig.AddScheme(u"Light"_ustr);
+ }
+ SwXTextDocument* pXTextDocument = createDoc();
+ int nFirstViewId = SfxLokHelper::getView();
+ ViewCallback aView1;
+ // Set first view to light scheme
+ {
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ SwView* pView = pDoc->GetDocShell()->GetView();
+ uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
+ uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
+ {
+ { "NewTheme", uno::Any(OUString("Light")) },
+ }
+ );
+ comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
+ }
+ // First view is in light scheme
+ assertTilePixelColor(pXTextDocument, 255, 255, COL_WHITE);
+ // Create second view
+ SfxLokHelper::createView();
+ int nSecondViewId = SfxLokHelper::getView();
+ ViewCallback aView2;
+ // Set second view to dark scheme
+ {
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ SwView* pView = pDoc->GetDocShell()->GetView();
+ uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
+ uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
+ {
+ { "NewTheme", uno::Any(OUString("Dark")) },
+ }
+ );
+ comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
+ }
+ assertTilePixelColor(pXTextDocument, 255, 255, aDarkColor);
+ // First view still in light scheme
+ SfxLokHelper::setView(nFirstViewId);
+ assertTilePixelColor(pXTextDocument, 255, 255, COL_WHITE);
+ // Second view still in dark scheme
+ SfxLokHelper::setView(nSecondViewId);
+ assertTilePixelColor(pXTextDocument, 255, 255, aDarkColor);
+ // Switch second view back to light scheme
+ {
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ SwView* pView = pDoc->GetDocShell()->GetView();
+ uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
+ uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
+ {
+ { "NewTheme", uno::Any(OUString("Light")) },
+ }
+ );
+ comphelper::dispatchCommand(".uno:ChangeTheme", xFrame, aPropertyValues);
+ }
+ // Now in light scheme
+ assertTilePixelColor(pXTextDocument, 255, 255, COL_WHITE);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSetViewGraphicSelection)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("frame.odt");
+ int nView1 = SfxLokHelper::getView();
+ ViewCallback aView1;
+ // Create a second view, and switch back to the first view.
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering({});
+ SfxLokHelper::setView(nView1);
+
+ // Mark the textframe in the first view.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ pView->MarkObj(pObject, pView->GetSdrPageView());
+ CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
+
+ // Now start to switch to the second view (part of setView()).
+ pWrtShell->ShellLoseFocus();
+ // This failed, mark handles were hidden in the first view.
+ CPPUNIT_ASSERT(!pView->areMarkHandlesHidden());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewGraphicSelection)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("frame.odt");
+ ViewCallback aView1;
+
+ // Mark the textframe in the first view.
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ SdrView* pView = pWrtShell->GetDrawView();
+ aView1.m_bGraphicSelection = true;
+ pView->MarkObj(pObject, pView->GetSdrPageView());
+ pWrtShell->HideCursor();
+ CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
+
+ // Create a second view.
+ SfxLokHelper::createView();
+ // This was false, creating a second view cleared the selection of the
+ // first one.
+ CPPUNIT_ASSERT(aView1.m_bGraphicSelection);
+
+ // Make sure that the hidden text cursor isn't visible in the second view, either.
+ ViewCallback aView2(SfxViewShell::Current(),
+ [](ViewCallback& rView) { rView.m_bViewCursorVisible = true; });
+ // This was true, the second view didn't get the visibility of the text
+ // cursor of the first view.
+ CPPUNIT_ASSERT(!aView2.m_bViewCursorVisible);
+ // This was false, the second view didn't get the graphic selection of the
+ // first view.
+ CPPUNIT_ASSERT(aView2.m_bGraphicViewSelection);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCreateViewTextSelection)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ ViewCallback aView1;
+
+ // Create a text selection:
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ // Move the cursor into the second word.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 5, /*bBasicCall=*/false);
+ // Create a selection on the word.
+ pWrtShell->SelWrd();
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ // Did we indeed manage to select the second word?
+ CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
+
+ // Create a second view.
+ SfxLokHelper::createView();
+
+ // Make sure that the text selection is visible in the second view.
+ ViewCallback aView2;
+ // This failed, the second view didn't get the text selection of the first view.
+ CPPUNIT_ASSERT(!aView2.m_aViewSelection.isEmpty());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineColors)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+
+ // Turn on track changes, type "zzz" at the end.
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->EndOfSection();
+ pWrtShell->Insert("zzz");
+
+ // Assert that info about exactly one author is returned.
+ tools::JsonWriter aJsonWriter;
+ pXTextDocument->getTrackedChangeAuthors(aJsonWriter);
+ std::stringstream aStream((std::string(aJsonWriter.finishAndGetAsOString())));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("authors").size());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentEndTextEdit)
+{
+ // Create a document, type a character and remember the cursor position.
+ SwXTextDocument* pXTextDocument = createDoc();
+ ViewCallback aView1;
+ emulateTyping(*pXTextDocument, u"x");
+ tools::Rectangle aBodyCursor = aView1.m_aOwnCursor;
+
+ // Create a comment and type a character there as well.
+ const int nCtrlAltC = KEY_MOD1 + KEY_MOD2 + 512 + 'c' - 'a';
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'c', nCtrlAltC);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'c', nCtrlAltC);
+ emulateTyping(*pXTextDocument, u"x");
+ // End comment text edit by clicking in the body text area, and assert that
+ // no unexpected cursor callbacks are emitted at origin (top left corner of
+ // the document).
+ aView1.m_bOwnCursorAtOrigin = false;
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aBodyCursor.Left(), aBodyCursor.Top(), 1, MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aBodyCursor.Left(), aBodyCursor.Top(), 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+ // This failed, the cursor was at 0, 0 at some point during end text edit
+ // of the comment.
+ CPPUNIT_ASSERT(!aView1.m_bOwnCursorAtOrigin);
+
+ // Hit enter and expect invalidation.
+ Scheduler::ProcessEventsToIdle();
+ aView1.m_bTilesInvalidated = false;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentInsert)
+{
+ // Load a document with an as-char image in it.
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+ SwXTextDocument* pXTextDocument = createDoc("image-comment.odt");
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ SwView* pView = pDoc->GetDocShell()->GetView();
+
+ selectShape(1);
+
+ // Add a comment.
+ uno::Reference<frame::XFrame> xFrame = pView->GetViewFrame().GetFrame().GetFrameInterface();
+ uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
+ {
+ {"Text", uno::Any(OUString("some text"))},
+ {"Author", uno::Any(OUString("me"))},
+ });
+ ViewCallback aView;
+ comphelper::dispatchCommand(".uno:InsertAnnotation", xFrame, aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+ OString aAnchorPos(aView.m_aComment.get_child("anchorPos").get_value<std::string>());
+ // Without the accompanying fix in place, this test would have failed with
+ // - Expected: 1418, 1418, 0, 0
+ // - Actual : 1418, 1418, 1024, 1024
+ // i.e. the anchor position was a non-empty rectangle.
+ CPPUNIT_ASSERT_EQUAL("1418, 1418, 0, 0"_ostr, aAnchorPos);
+ comphelper::LibreOfficeKit::setTiledAnnotations(true);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCursorPosition)
+{
+ // Load a document and register a callback, should get an own cursor.
+ SwXTextDocument* pXTextDocument = createDoc();
+ ViewCallback aView1;
+
+ // Create a second view, so the first view gets a collaborative cursor.
+ SfxLokHelper::createView();
+ pXTextDocument->initializeForTiledRendering({});
+ ViewCallback aView2;
+
+ // Make sure the two are exactly the same.
+ // This failed, own cursor was at '1418, 1418', collaborative cursor was at
+ // '1425, 1425', due to pixel alignment.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.toString(), aView1.m_aViewCursor.toString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPaintCallbacks)
+{
+ // Test that paintTile() never results in callbacks, which can cause a
+ // paint <-> invalidate loop.
+
+ // Load a document and register a callback for the first view.
+ SwXTextDocument* pXTextDocument = createDoc();
+ ViewCallback aView1;
+
+ // Create a second view and paint a tile on that second view.
+ SfxLokHelper::createView();
+ int nCanvasWidth = 256;
+ int nCanvasHeight = 256;
+ std::vector<unsigned char> aBuffer(nCanvasWidth * nCanvasHeight * 4);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer.data());
+ // Make sure that painting a tile in the second view doesn't invoke
+ // callbacks on the first view.
+ aView1.m_bCalled = false;
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0, /*nTilePosY=*/0, /*nTileWidth=*/3840, /*nTileHeight=*/3840);
+ CPPUNIT_ASSERT(!aView1.m_bCalled);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testUndoRepairResult)
+{
+ // Load a document and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ rtl::Reference<TestResultListener> pResult2 = new TestResultListener();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ int nView2 = SfxLokHelper::getView();
+
+ // Insert a character in the second view.
+ SfxLokHelper::setView(nView2);
+ emulateTyping(*pXTextDocument, u"b");
+
+ // Insert a character in the first view.
+ SfxLokHelper::setView(nView1);
+ emulateTyping(*pXTextDocument, u"a");
+
+ // Assert that by default the second view can't undo the action.
+ SfxLokHelper::setView(nView2);
+ comphelper::dispatchCommand(".uno:Undo", {}, pResult2);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedoRepairResult)
+{
+ // Load a document and create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ rtl::Reference<TestResultListener> pResult2 = new TestResultListener();
+ pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ int nView2 = SfxLokHelper::getView();
+
+ // Insert a character in the second view.
+ SfxLokHelper::setView(nView2);
+ emulateTyping(*pXTextDocument, u"b");
+
+ // Insert a character in the first view.
+ SfxLokHelper::setView(nView1);
+ emulateTyping(*pXTextDocument, u"a");
+
+ comphelper::dispatchCommand(".uno:Undo", {}, pResult2);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(0), pResult2->m_nDocRepair);
+
+ // Assert that by default the second view can't redo the action.
+ SfxLokHelper::setView(nView2);
+ comphelper::dispatchCommand(".uno:Redo", {}, pResult2);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pResult2->m_nDocRepair);
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+namespace {
+
+ void checkUndoRepairStates(SwXTextDocument* pXTextDocument, SwView* pView1, SwView* pView2)
+ {
+ SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
+ SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
+ // first view, undo enabled
+ pView1->GetState(aItemSet1);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
+ const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
+ CPPUNIT_ASSERT(!pUnsetItem);
+ // second view, undo conflict
+ pView2->GetState(aItemSet2);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
+ const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
+ CPPUNIT_ASSERT(pUInt32Item);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
+ };
+
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDisableUndoRepair)
+{
+ // Create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ ViewCallback aView1;
+ SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
+ CPPUNIT_ASSERT(pView1);
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ ViewCallback aView2;
+ SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
+ CPPUNIT_ASSERT(pView2);
+ int nView2 = SfxLokHelper::getView();
+
+ {
+ SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
+ SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
+ pView1->GetState(aItemSet1);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet1.GetItemState(SID_UNDO));
+ pView2->GetState(aItemSet2);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED, aItemSet2.GetItemState(SID_UNDO));
+ }
+
+ // Insert a character in the first view.
+ SfxLokHelper::setView(nView1);
+ emulateTyping(*pXTextDocument, u"k");
+ checkUndoRepairStates(pXTextDocument, pView1, pView2);
+
+ // Insert a character in the second view.
+ SfxLokHelper::setView(nView2);
+ emulateTyping(*pXTextDocument, u"u");
+ {
+ SfxItemSet aItemSet1(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
+ SfxItemSet aItemSet2(pXTextDocument->GetDocShell()->GetDoc()->GetAttrPool(), svl::Items<SID_UNDO, SID_UNDO>);
+ // second view, undo enabled
+ pView2->GetState(aItemSet2);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet2.GetItemState(SID_UNDO));
+ const SfxUInt32Item *pUnsetItem = dynamic_cast<const SfxUInt32Item*>(aItemSet2.GetItem(SID_UNDO));
+ CPPUNIT_ASSERT(!pUnsetItem);
+ // first view, undo conflict
+ pView1->GetState(aItemSet1);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, aItemSet1.GetItemState(SID_UNDO));
+ const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item*>(aItemSet1.GetItem(SID_UNDO));
+ CPPUNIT_ASSERT(pUInt32Item);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(SID_REPAIRPACKAGE), pUInt32Item->GetValue());
+ }
+
+ // Insert a character in the first view.
+ SfxLokHelper::setView(nView1);
+ emulateTyping(*pXTextDocument, u"l");
+ checkUndoRepairStates(pXTextDocument, pView1, pView2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAllTrackedChanges)
+{
+ // Load a document.
+ createDoc("dummy.fodt");
+
+ uno::Reference<beans::XPropertySet> xPropSet(mxComponent, uno::UNO_QUERY);
+ xPropSet->setPropertyValue("RecordChanges", uno::Any(true));
+
+ // view #1
+ SwView* pView1 = dynamic_cast<SwView*>(SfxViewShell::Current());
+ CPPUNIT_ASSERT(pView1);
+ SwWrtShell* pWrtShell1 = pView1->GetWrtShellPtr();
+
+ // view #2
+ int nView1 = SfxLokHelper::getView();
+ int nView2 = SfxLokHelper::createView();
+ SwView* pView2 = dynamic_cast<SwView*>(SfxViewShell::Current());
+ CPPUNIT_ASSERT(pView2);
+ CPPUNIT_ASSERT(pView1 != pView2);
+ SwWrtShell* pWrtShell2 = pView2->GetWrtShellPtr();
+ // Insert text and reject all
+ {
+ pWrtShell1->StartOfSection();
+ pWrtShell1->Insert("hxx");
+
+ pWrtShell2->EndOfSection();
+ pWrtShell2->Insert("cxx");
+ }
+
+ // Get the redline
+ const SwRedlineTable& rTable = pWrtShell2->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
+ {
+ SfxVoidItem aItem(FN_REDLINE_REJECT_ALL);
+ pView1->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_REJECT_ALL,
+ SfxCallMode::SYNCHRON, { &aItem });
+ }
+
+ // The reject all was performed.
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
+ {
+ SwShellCursor* pShellCursor = pWrtShell1->getShellCursor(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+ }
+
+ // Insert text and accept all
+ {
+ pWrtShell1->StartOfSection();
+ pWrtShell1->Insert("hyy");
+
+ pWrtShell2->EndOfSection();
+ pWrtShell2->Insert("cyy");
+ }
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), rTable.size());
+ {
+ SfxVoidItem aItem(FN_REDLINE_ACCEPT_ALL);
+ pView1->GetViewFrame().GetDispatcher()->ExecuteList(FN_REDLINE_ACCEPT_ALL,
+ SfxCallMode::SYNCHRON, { &aItem });
+ }
+
+ // The accept all was performed
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(0), rTable.size());
+ {
+ SwShellCursor* pShellCursor = pWrtShell2->getShellCursor(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("hyyAaa bbb.cyy"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+ }
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDocumentRepair)
+{
+ // Create two views.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ // view #1
+ SfxViewShell* pView1 = SfxViewShell::Current();
+
+ // view #2
+ int nView1 = SfxLokHelper::getView();
+ SfxLokHelper::createView();
+ SfxViewShell* pView2 = SfxViewShell::Current();
+ int nView2 = SfxLokHelper::getView();
+ CPPUNIT_ASSERT(pView1 != pView2);
+ {
+ std::unique_ptr<SfxBoolItem> pItem1;
+ std::unique_ptr<SfxBoolItem> pItem2;
+ pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
+ pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
+ CPPUNIT_ASSERT(pItem1);
+ CPPUNIT_ASSERT(pItem2);
+ CPPUNIT_ASSERT_EQUAL(false, pItem1->GetValue());
+ CPPUNIT_ASSERT_EQUAL(false, pItem2->GetValue());
+ }
+
+ // Insert a character in the second view.
+ SfxLokHelper::setView(nView2);
+ emulateTyping(*pXTextDocument, u"u");
+ {
+ std::unique_ptr<SfxBoolItem> pItem1;
+ std::unique_ptr<SfxBoolItem> pItem2;
+ pView1->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem1);
+ pView2->GetViewFrame().GetBindings().QueryState(SID_DOC_REPAIR, pItem2);
+ CPPUNIT_ASSERT(pItem1);
+ CPPUNIT_ASSERT(pItem2);
+ CPPUNIT_ASSERT_EQUAL(true, pItem1->GetValue());
+ CPPUNIT_ASSERT_EQUAL(true, pItem2->GetValue());
+ }
+
+ SfxLokHelper::setView(nView1);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+ SfxLokHelper::setView(nView2);
+ SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
+}
+
+namespace {
+
+ void checkPageHeaderOrFooter(const SfxViewShell* pViewShell, TypedWhichId<SfxStringListItem> nWhich, bool bValue)
+ {
+ uno::Sequence<OUString> aSeq;
+ SfxPoolItemHolder aResult;
+ pViewShell->GetDispatcher()->QueryState(nWhich, aResult);
+ const SfxStringListItem* pListItem(static_cast<const SfxStringListItem*>(aResult.getItem()));
+ CPPUNIT_ASSERT(pListItem);
+ pListItem->GetStringList(aSeq);
+ if (bValue)
+ {
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aSeq.getLength());
+ CPPUNIT_ASSERT_EQUAL(OUString("Default Page Style"), aSeq[0]);
+ }
+ else
+ CPPUNIT_ASSERT(!aSeq.hasElements());
+ };
+
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageHeader)
+{
+ createDoc("dummy.fodt");
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ // Check Page Header State
+ checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, false);
+ // Insert Page Header
+ {
+ SfxStringItem aStyle(FN_INSERT_PAGEHEADER, "Default Page Style");
+ SfxBoolItem aItem(FN_PARAM_1, true);
+ pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEHEADER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aStyle, &aItem});
+ }
+ // Check Page Header State
+ checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, true);
+
+ // Remove Page Header
+ {
+ SfxStringItem aStyle(FN_INSERT_PAGEHEADER, "Default Page Style");
+ SfxBoolItem aItem(FN_PARAM_1, false);
+ pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEHEADER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aStyle, &aItem});
+ }
+ // Check Page Header State
+ checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEHEADER, false);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageFooter)
+{
+ createDoc("dummy.fodt");
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ // Check Page Footer State
+ checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, false);
+ // Insert Page Footer
+ {
+ SfxStringItem aPageStyle(FN_INSERT_PAGEFOOTER, "Default Page Style");
+ SfxBoolItem aItem(FN_PARAM_1, true);
+ pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEFOOTER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aPageStyle, &aItem});
+ }
+ // Check Page Footer State
+ checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, true);
+
+ // Remove Page Footer
+ {
+ SfxStringItem aPageStyle(FN_INSERT_PAGEFOOTER, "Default Page Style");
+ SfxBoolItem aItem(FN_PARAM_1, false);
+ pViewShell->GetDispatcher()->ExecuteList(FN_INSERT_PAGEFOOTER, SfxCallMode::API | SfxCallMode::SYNCHRON, {&aPageStyle, &aItem});
+ }
+ // Check Footer State
+ checkPageHeaderOrFooter(pViewShell, FN_INSERT_PAGEFOOTER, false);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf115088)
+{
+ // We have three lines in the test document and we try to copy the second and third line
+ // To the beginning of the document
+ SwXTextDocument* pXTextDocument = createDoc("tdf115088.odt");
+
+ // Select and copy second and third line
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN | KEY_SHIFT);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN | KEY_SHIFT);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT | KEY_SHIFT);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT | KEY_SHIFT);
+ Scheduler::ProcessEventsToIdle();
+ comphelper::dispatchCommand(".uno:Copy", uno::Sequence<beans::PropertyValue>());
+
+ // Move cursor to the beginning of the first line and paste
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
+ Scheduler::ProcessEventsToIdle();
+ comphelper::dispatchCommand(".uno:PasteUnformatted", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ // Check the resulting text in the document. (it was 1Text\n1\n1\n1)
+ CPPUNIT_ASSERT_EQUAL(OUString("1\n1Text\n1\n1"), pXTextDocument->getText()->getString());
+
+ mxComponent->dispose();
+ mxComponent.clear();
+ comphelper::LibreOfficeKit::setActive(false);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineField)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+
+ // Turn on track changes and type "x".
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::Any(true));
+
+ SwDateTimeField aDate(static_cast<SwDateTimeFieldType*>(pWrtShell->GetFieldType(0, SwFieldIds::DateTime)));
+ //aDate->SetDateTime(::DateTime(::DateTime::SYSTEM));
+ pWrtShell->InsertField2(aDate);
+
+ // Get the redline just created
+ const SwRedlineTable& rTable = pWrtShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());
+ SwRangeRedline* pRedline = rTable[0];
+ CPPUNIT_ASSERT(pRedline->GetDescr().indexOf(aDate.GetFieldName())!= -1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMESupport)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
+
+ SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
+ assert(pView);
+ SwWrtShell* pWrtShell = pView->GetWrtShellPtr();
+
+ // sequence of chinese IME compositions when 'nihao' is typed in an IME
+ const std::vector<OString> aUtf8Inputs{ "年"_ostr, "你"_ostr, "你好"_ostr, "你哈"_ostr, "你好"_ostr, "你好"_ostr };
+ std::vector<OUString> aInputs;
+ std::transform(aUtf8Inputs.begin(), aUtf8Inputs.end(),
+ std::back_inserter(aInputs), [](OString aInput) {
+ return OUString::fromUtf8(aInput);
+ });
+ for (const auto& aInput: aInputs)
+ {
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, aInput);
+ }
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ // the cursor should be at position 2nd
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), pShellCursor->GetPoint()->GetContentIndex());
+
+ // content contains only the last IME composition, not all
+ CPPUNIT_ASSERT_EQUAL(OUString(aInputs[aInputs.size() - 1] + "Aaa bbb."), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMEFormattingAtEndOfParagraph)
+{
+ comphelper::LibreOfficeKit::setActive();
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
+
+ SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
+ assert(pView);
+ SwWrtShell* pWrtShell = pView->GetWrtShellPtr();
+
+ // delete all characters
+
+ for (int i = 0; i < 9; i++)
+ {
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
+ }
+
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ // status: "a"
+
+ comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // status: "a<bold>b</bold>\n"
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ std::unique_ptr<SvxWeightItem> pWeightItem;
+ pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem);
+ CPPUNIT_ASSERT(pWeightItem);
+
+ CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem->GetWeight());
+
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // status: "a<bold>b</bold>\n
+ // <bold>a</bold>\n"
+
+ comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ std::unique_ptr<SvxWeightItem> pWeightItem2;
+ pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem2);
+ CPPUNIT_ASSERT(pWeightItem2);
+
+ CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem2->GetWeight());
+
+ // status: "a<bold>b</bold>\n
+ // <bold>a</bold>\n"
+ // b"
+
+ comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ std::unique_ptr<SvxWeightItem> pWeightItem3;
+ pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem3);
+ CPPUNIT_ASSERT(pWeightItem3);
+
+ CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem3->GetWeight());
+
+ comphelper::dispatchCommand(".uno:Bold", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ std::unique_ptr<SvxWeightItem> pWeightItem4;
+ pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem4);
+ CPPUNIT_ASSERT(pWeightItem4);
+
+ CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem4->GetWeight());
+
+ // status: "a<bold>b</bold>\n
+ // <bold>a</bold>\n"
+ // b<bold>a</bold>b"
+
+ // the cursor should be at position 3rd
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), pShellCursor->GetPoint()->GetContentIndex());
+
+ // check the content
+ CPPUNIT_ASSERT_EQUAL(OUString("bab"), pShellCursor->GetPoint()->GetNode().GetTextNode()->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIMEFormattingAfterHeader)
+{
+ comphelper::LibreOfficeKit::setActive();
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ VclPtr<vcl::Window> pDocWindow = pXTextDocument->getDocWindow();
+
+ SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
+ assert(pView);
+
+ // delete all characters
+
+ comphelper::dispatchCommand(".uno:SelectAll", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
+
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "a");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // status: "a\n"
+
+ comphelper::dispatchCommand(
+ ".uno:StyleApply?Style:string=Heading 2&FamilyName:string=ParagraphStyles",
+ uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "b");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+ Scheduler::ProcessEventsToIdle();
+
+ std::unique_ptr<SvxWeightItem> pWeightItem;
+ pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem);
+ CPPUNIT_ASSERT(pWeightItem);
+
+ CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_BOLD, pWeightItem->GetWeight());
+
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // status: "a\n
+ // <h2>bb</h2>\n"
+
+ pDocWindow->PostExtTextInputEvent(VclEventId::ExtTextInput, "c");
+ pDocWindow->PostExtTextInputEvent(VclEventId::EndExtTextInput, "");
+ Scheduler::ProcessEventsToIdle();
+
+ // status: "a\n
+ // <h2>bb</h2>\n"
+ // c"
+
+ std::unique_ptr<SvxWeightItem> pWeightItem2;
+ pView->GetViewFrame().GetBindings().QueryState(SID_ATTR_CHAR_WEIGHT, pWeightItem2);
+ CPPUNIT_ASSERT(pWeightItem2);
+
+ CPPUNIT_ASSERT_EQUAL(FontWeight::WEIGHT_NORMAL, pWeightItem2->GetWeight());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSplitNodeRedlineCallback)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("splitnode_redline_callback.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // 1. test case
+ // Move cursor between the two tracked changes
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ Scheduler::ProcessEventsToIdle();
+
+ // Add a new line
+ m_nRedlineTableEntryModified = 0;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // Assert that we get a notification about redline modification
+ // The redline after the inserted node gets a different vertical position
+ CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
+
+ // 2. test case
+ // Move cursor back to the first line, so adding new line will affect both tracked changes
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
+ Scheduler::ProcessEventsToIdle();
+
+ // Add a new line
+ m_nRedlineTableEntryModified = 0;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
+
+ // 3. test case
+ // Move cursor to the end of the document, so adding a new line won't affect any tracked changes
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_END | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_END | KEY_MOD1);
+ Scheduler::ProcessEventsToIdle();
+
+ // Add a new line
+ m_nRedlineTableEntryModified = 0;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(0, m_nRedlineTableEntryModified);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeleteNodeRedlineCallback)
+{
+ // Load a document.
+ SwXTextDocument* pXTextDocument = createDoc("removenode_redline_callback.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // 1. test case
+ // Move cursor between the two tracked changes
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DOWN);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DOWN);
+ Scheduler::ProcessEventsToIdle();
+
+ // Remove one (empty) line
+ m_nRedlineTableEntryModified = 0;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
+ Scheduler::ProcessEventsToIdle();
+
+ // Assert that we get a notification about redline modification
+ // The redline after the removed node gets a different vertical position
+ CPPUNIT_ASSERT_EQUAL(1, m_nRedlineTableEntryModified);
+
+ // 2. test case
+ // Move cursor back to the first line, so removing one line will affect both tracked changes
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_HOME | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_HOME | KEY_MOD1);
+ Scheduler::ProcessEventsToIdle();
+
+ // Remove a new line
+ m_nRedlineTableEntryModified = 0;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(2, m_nRedlineTableEntryModified);
+
+ // 3. test case
+ // Move cursor to the end of the document, so removing one line won't affect any tracked changes
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_END | KEY_MOD1);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_END | KEY_MOD1);
+ Scheduler::ProcessEventsToIdle();
+
+ // Remove a line
+ m_nRedlineTableEntryModified = 0;
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_BACKSPACE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_BACKSPACE);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(0, m_nRedlineTableEntryModified);
+}
+
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testVisCursorInvalidation)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ ViewCallback aView1;
+ int nView1 = SfxLokHelper::getView();
+
+ SfxLokHelper::createView();
+ int nView2 = SfxLokHelper::getView();
+ ViewCallback aView2;
+ Scheduler::ProcessEventsToIdle();
+
+ // Move visible cursor in the first view
+ SfxLokHelper::setView(nView1);
+ Scheduler::ProcessEventsToIdle();
+
+ aView1.m_bOwnCursorInvalidated = false;
+ aView1.m_bViewCursorInvalidated = false;
+ aView2.m_bOwnCursorInvalidated = false;
+ aView2.m_bViewCursorInvalidated = false;
+
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT(!aView1.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
+ CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(!aView2.m_bOwnCursorInvalidated);
+
+ // Insert text in the second view which moves the other view's cursor too
+ SfxLokHelper::setView(nView2);
+
+ Scheduler::ProcessEventsToIdle();
+ aView1.m_bOwnCursorInvalidated = false;
+ aView1.m_bViewCursorInvalidated = false;
+ aView2.m_bOwnCursorInvalidated = false;
+ aView2.m_bViewCursorInvalidated = false;
+
+ emulateTyping(*pXTextDocument, u"x");
+
+ CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
+ CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
+ // Check that views have correct location for the other's cursor.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
+ CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
+ // Their cursors should be on the same line, first view's more to the right.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
+ CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
+
+ // Do the same as before, but set the related compatibility flag first
+ SfxLokHelper::setView(nView2);
+
+ comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
+
+ Scheduler::ProcessEventsToIdle();
+ aView1.m_bOwnCursorInvalidated = false;
+ aView1.m_bViewCursorInvalidated = false;
+ aView2.m_bOwnCursorInvalidated = false;
+ aView2.m_bViewCursorInvalidated = false;
+
+ emulateTyping(*pXTextDocument, u"x");
+
+ CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
+ CPPUNIT_ASSERT_EQUAL(nView2, aView1.m_nOwnCursorInvalidatedBy);
+ CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
+ CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
+ CPPUNIT_ASSERT_EQUAL(nView2, aView2.m_nOwnCursorInvalidatedBy);
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
+ CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
+ // Their cursors should be on the same line, first view's more to the right.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
+ CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
+
+ comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(false);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDeselectCustomShape)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+ Point aStart = pShellCursor->GetSttPos();
+ aStart.setX(aStart.getX() - 1000);
+ aStart.setY(aStart.getY() - 1000);
+
+ comphelper::dispatchCommand(".uno:BasicShapes.hexagon", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount());
+
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSemiTransparent)
+{
+ // Load a document where the top left tile contains a semi-transparent rectangle shape.
+ SwXTextDocument* pXTextDocument = createDoc("semi-transparent.odt");
+
+ // Render a larger area, and then get the color of the bottom right corner of our tile.
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ size_t nTileSize = 256;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+ Color aColor(pAccess->GetPixel(255, 255));
+
+ // Without the accompanying fix in place, this test would have failed with 'Expected greater or
+ // equal than: 190; Actual: 159'. This means the semi-transparent gray rectangle was darker than
+ // expected, as it was painted twice.
+ CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetRed()));
+ CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetGreen()));
+ CPPUNIT_ASSERT_GREATEREQUAL(190, static_cast<int>(aColor.GetBlue()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHighlightNumbering)
+{
+ // Load a document where the top left tile contains a semi-transparent rectangle shape.
+ SwXTextDocument* pXTextDocument = createDoc("tdf114799_highlight.docx");
+
+ // Render a larger area, and then get the color of the bottom right corner of our tile.
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ size_t nTileSize = 256;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+
+ // Yellow highlighting over numbering
+ Color aColor(pAccess->GetPixel(103, 148));
+ CPPUNIT_ASSERT_EQUAL(COL_YELLOW, aColor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHighlightNumbering_shd)
+{
+ // Load a document where the top left tile contains a semi-transparent rectangle shape.
+ SwXTextDocument* pXTextDocument = createDoc("tdf114799_shd.docx");
+
+ // Render a larger area, and then get the color of the bottom right corner of our tile.
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ size_t nTileSize = 256;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+
+ // No highlighting over numbering - w:shd does not apply to numbering.
+ Color aColor(pAccess->GetPixel(103, 148));
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, aColor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPilcrowRedlining)
+{
+ // Load a document where the top left tile contains
+ // paragraph and line break symbols with redlining.
+ SwXTextDocument* pXTextDocument = createDoc("pilcrow-redlining.fodt");
+
+ // show non printing characters, including pilcrow and
+ // line break symbols with redlining
+ comphelper::dispatchCommand(".uno:ControlCodes", {});
+
+ // Render a larger area, and then get the color of the bottom right corner of our tile.
+ size_t nCanvasWidth = 2048;
+ size_t nCanvasHeight = 1024;
+ size_t nTileSize = 512;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(100, 100), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+
+ const char* aTexts[] = {
+ "Insert paragraph break",
+ "Insert paragraph break (empty line)",
+ "Delete paragraph break",
+ "Delete paragraph break (empty line)",
+ "Insert line break",
+ "Insert line break (empty line)",
+ "Delete line break",
+ "Delete line break (empty line)"
+ };
+
+ // Check redlining (strike out and underline) over the paragraph and line break symbols
+ for (int nLine = 0; nLine < 8; ++nLine)
+ {
+ bool bHasRedlineColor = false;
+ for (int i = 0; i < 36 && !bHasRedlineColor; ++i)
+ {
+ int nY = 96 + nLine * 36 + i;
+ for (sal_uInt32 j = 0; j < nTileSize - 1; ++j)
+ {
+ Color aColor(pAccess->GetPixel(nY, j));
+ Color aColor2(pAccess->GetPixel(nY+1, j));
+ Color aColor3(pAccess->GetPixel(nY, j+1));
+ Color aColor4(pAccess->GetPixel(nY+1, j+1));
+ // 4-pixel same color square sign strike out or underline of redlining
+ // if its color is not white, black or non-printing character color
+ if ( aColor == aColor2 && aColor == aColor3 && aColor == aColor4 &&
+ aColor != COL_WHITE && aColor != COL_BLACK &&
+ aColor != NON_PRINTING_CHARACTER_COLOR )
+ {
+ bHasRedlineColor = true;
+ break;
+ }
+ }
+ }
+
+ CPPUNIT_ASSERT_MESSAGE(aTexts[nLine], bHasRedlineColor);
+ }
+
+ comphelper::dispatchCommand(".uno:ControlCodes", {});
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShowHiddenCharsWhenShowFormatting)
+{
+ // In LOKit, ignore the config setting for
+ // Tools - Options - Writer - Formatting Aids - Display Formatting - Hidden characters
+ // and always show hidden content when showing pilcrow formatting
+
+ createSwDoc("hiddenLoremIpsum.docx");
+
+ // Since LOKit is active in TiledRendering, turning on "Show formatting" will show hidden text.
+ comphelper::dispatchCommand(".uno:ControlCodes", {}); // show format marks
+ Scheduler::ProcessEventsToIdle();
+
+ // Without this patch, no body text would be visible - so only 1 page instead of 3.
+ CPPUNIT_ASSERT_EQUAL(3, getPages());
+
+ comphelper::dispatchCommand(".uno:ControlCodes", {});
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDoubleUnderlineAndStrikeOut)
+{
+ // Load a document where the tracked text moving is visible with
+ // double underline and strike out character formatting
+ SwXTextDocument* pXTextDocument = createDoc("double-underline_and_strike-out.fodt");
+
+ // Render a larger area, and then get the color of the bottom right corner of our tile.
+ size_t nCanvasWidth = 700;
+ size_t nCanvasHeight = 350;
+ size_t nTileSize = 350;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+ bool bGreenLine = false;
+ size_t nGreenLine = 0;
+ // count green horizontal lines by tracking a column of pixels counting the
+ // separated continuous green pixel sequences.
+ for (size_t nLine = 0; nLine < nTileSize; ++nLine)
+ {
+ Color aColor(pAccess->GetPixel(nLine, 100));
+ if ( aColor == COL_GREEN )
+ {
+ if ( bGreenLine == false )
+ {
+ ++nGreenLine;
+ bGreenLine = true;
+ }
+ }
+ else
+ bGreenLine = false;
+ }
+ // tdf#152214 this was 0 (missing double underline and double strike out)
+ CPPUNIT_ASSERT_EQUAL(size_t(4), nGreenLine);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf43244_SpacesOnMargin)
+{
+ // Load a document where the top left tile contains
+ // paragraph and line break symbols with redlining.
+ SwXTextDocument* pXTextDocument = createDoc("tdf43244_SpacesOnMargin.odt");
+
+ // show non printing characters, including pilcrow and
+ // line break symbols with redlining
+ comphelper::dispatchCommand(".uno:ControlCodes", {});
+
+ // Render a larger area, and then get the colors from the right side of the page.
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ size_t nTileSize = 64;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(730, 120), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+
+ //Test if we see any spaces on the right margin in a 47x48 rectangle
+ bool bSpaceFound = false;
+ for (int i = 1; i < 48 && !bSpaceFound; i++)
+ {
+ for (int j = 0; j < i; j++)
+ {
+ Color aColor2(pAccess->GetPixel(j, i));
+ Color aColor1(pAccess->GetPixel(i, j + 1));
+
+ if (aColor1.GetRed() < 255 || aColor2.GetRed() < 255)
+ {
+ bSpaceFound = true;
+ break;
+ }
+ }
+ }
+ CPPUNIT_ASSERT(bSpaceFound);
+
+ comphelper::dispatchCommand(".uno:ControlCodes", {});
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testClipText)
+{
+ // Load a document where the top left tile contains table text with
+ // too small line height, but with top and bottom paragraph margins,
+ // avoiding of clipping top and bottom parts of the characters.
+ SwXTextDocument* pXTextDocument = createDoc("tdf117448.fodt");
+
+ // Render a larger area, and then get the top and bottom of the text in that tile
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ size_t nTileSize = 256;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/15360, /*nTileHeight=*/7680);
+ pDevice->EnableMapMode(false);
+ Bitmap aBitmap = pDevice->GetBitmap(Point(0, 0), Size(nTileSize, nTileSize));
+ BitmapScopedReadAccess pAccess(aBitmap);
+
+ // check top margin, it's not white completely (i.e. showing top of letter "T")
+ bool bClipTop = true;
+ for (int i = 0; i < 150; i++)
+ {
+ Color aTopTextColor(pAccess->GetPixel(98, 98 + i));
+ if (aTopTextColor.GetRed() < 255)
+ {
+ bClipTop = false;
+ break;
+ }
+ }
+ CPPUNIT_ASSERT(!bClipTop);
+ // switch off because of false alarm on some platform, maybe related to font replacements
+#if 0
+ // check bottom margin, it's not white completely (i.e. showing bottom of letter "g")
+ bool bClipBottom = true;
+ for (int i = 0; i < 150; i++)
+ {
+ Color aBottomTextColor(pAccess->GetPixel(110, 98 + i));
+ if (aBottomTextColor.R < 255)
+ {
+ bClipBottom = false;
+ break;
+ }
+ }
+ CPPUNIT_ASSERT(!bClipBottom);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAnchorTypes)
+{
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+ selectShape(1);
+
+ SwDoc* pDoc = pXTextDocument->GetDocShell()->GetDoc();
+ SwView* pView = pXTextDocument->GetDocShell()->GetView();
+ SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<FN_TOOL_ANCHOR_PAGE, FN_TOOL_ANCHOR_PAGE>);
+ SfxBoolItem aItem(FN_TOOL_ANCHOR_PAGE);
+ aSet.Put(aItem);
+ auto pShell = dynamic_cast<SwBaseShell*>(pView->GetCurShell());
+ pShell->GetState(aSet);
+ // Without the accompanying fix in place, this test would have failed, setting the anchor type
+ // to other than as/at-char was possible.
+ CPPUNIT_ASSERT(!aSet.HasItem(FN_TOOL_ANCHOR_PAGE));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testLanguageStatus)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwView* pView = pXTextDocument->GetDocShell()->GetView();
+ std::unique_ptr<SfxPoolItem> pItem;
+ pView->GetViewFrame().GetBindings().QueryState(SID_LANGUAGE_STATUS, pItem);
+ auto pStringListItem = dynamic_cast<SfxStringListItem*>(pItem.get());
+ CPPUNIT_ASSERT(pStringListItem);
+
+ uno::Sequence< OUString > aList;
+ pStringListItem->GetStringList(aList);
+ CPPUNIT_ASSERT_EQUAL(OUString("English (USA);en-US"), aList[0]);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineNotificationDuringSave)
+{
+ // Load a document with redlines which are hidden at a layout level.
+ // It's an empty document, just settings.xml and content.xml are custom.
+ SwXTextDocument* pXTextDocument = createDoc("redline-notification-during-save.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Save the document.
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer8");
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have never returned due to an infinite
+ // loop while sending not needed LOK notifications for redline changes during save.
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testHyperlink)
+{
+ comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
+ SwXTextDocument* pXTextDocument = createDoc("hyperlink.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pWrtShell->GetSfxViewShell()));
+ SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
+
+ Point aStart = pShellCursor->GetSttPos();
+ aStart.setX(aStart.getX() + 1800);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1,
+ MOUSE_LEFT, 0);
+ pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1,
+ MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL("hyperlink"_ostr, m_sHyperlinkText);
+ CPPUNIT_ASSERT_EQUAL("http://example.com/"_ostr, m_sHyperlinkLink);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testFieldmark)
+{
+ // Without the accompanying fix in place, this crashed on load.
+ createDoc("fieldmark.docx");
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButton)
+{
+ SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field.odt");
+ pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Move the cursor to trigger displaying of the field button.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
+
+ // Do a tile rendering to trigger the button message with a valid text area
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
+
+ CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aFormFieldButton)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
+
+ OString sType( aTree.get_child("type").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("drop-down"_ostr, sType);
+
+ OString sTextArea( aTree.get_child("textArea").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("1538, 1418, 1026, 275"_ostr, sTextArea);
+
+ boost::property_tree::ptree aItems = aTree.get_child("params").get_child("items");
+ CPPUNIT_ASSERT_EQUAL(size_t(6), aItems.size());
+
+ OStringBuffer aItemList;
+ for (auto &item : aItems)
+ {
+ aItemList.append(item.second.get_value<std::string>().c_str()
+ + OString::Concat(";"));
+ }
+ CPPUNIT_ASSERT_EQUAL("2019/2020;2020/2021;2021/2022;2022/2023;2023/2024;2024/2025;"_ostr, aItemList.toString());
+
+ OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("1"_ostr, sSelected);
+
+ OString sPlaceholder( aTree.get_child("params").get_child("placeholderText").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("No Item specified"_ostr, sPlaceholder);
+ }
+
+ // Move the cursor back so the button becomes hidden.
+ pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+
+ CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aFormFieldButton)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("hide"_ostr, sAction);
+
+ OString sType( aTree.get_child("type").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("drop-down"_ostr, sType);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonEditing)
+{
+ SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field2.odt");
+ pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Move the cursor to trigger displaying of the field button.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
+
+ // Do a tile rendering to trigger the button message with a valid text area
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
+
+ // The item with the index '1' is selected by default
+ CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aFormFieldButton)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("1"_ostr, sSelected);
+ }
+ m_aFormFieldButton = ""_ostr;
+
+ // Trigger a form field event to select a different item.
+ vcl::ITiledRenderable::StringMap aArguments;
+ aArguments["type"] = "drop-down";
+ aArguments["cmd"] = "selected";
+ aArguments["data"] = "3";
+ pXTextDocument->executeFromFieldEvent(aArguments);
+
+ // Do a tile rendering to trigger the button message.
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
+
+ CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aFormFieldButton)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("3"_ostr, sSelected);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonNoSelection)
+{
+ SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field_noselection.odt");
+ pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Move the cursor to trigger displaying of the field button.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
+
+ // Do a tile rendering to trigger the button message with a valid text area
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
+
+ // None of the items is selected
+ CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aFormFieldButton)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("-1"_ostr, sSelected);
+ }
+}
+
+static void lcl_extractHandleParameters(std::string_view selection, sal_Int32& id, sal_Int32& x, sal_Int32& y)
+{
+ OString extraInfo( selection.substr(selection.find("{")) );
+ std::stringstream aStream((std::string(extraInfo)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ boost::property_tree::ptree
+ handle0 = aTree
+ .get_child("handles")
+ .get_child("kinds")
+ .get_child("rectangle")
+ .get_child("1")
+ .begin()->second;
+ id = handle0.get_child("id").get_value<int>();
+ x = handle0.get_child("point").get_child("x").get_value<int>();
+ y = handle0.get_child("point").get_child("y").get_value<int>();
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMoveShapeHandle)
+{
+ comphelper::LibreOfficeKit::setActive();
+ SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObject = pPage->GetObj(0);
+ pWrtShell->SelectObj(Point(), 0, pObject);
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT(!m_ShapeSelection.isEmpty());
+ {
+ sal_Int32 id, x, y;
+ lcl_extractHandleParameters(m_ShapeSelection, id, x ,y);
+ sal_Int32 oldX = x;
+ sal_Int32 oldY = y;
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"HandleNum", uno::Any(id)},
+ {"NewPosX", uno::Any(x+1)},
+ {"NewPosY", uno::Any(y+1)}
+ }));
+ comphelper::dispatchCommand(".uno:MoveShapeHandle", aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(!m_ShapeSelection.isEmpty());
+ lcl_extractHandleParameters(m_ShapeSelection, id, x ,y);
+ CPPUNIT_ASSERT_EQUAL(x-1, oldX);
+ CPPUNIT_ASSERT_EQUAL(y-1, oldY);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownFormFieldButtonNoItem)
+{
+ SwXTextDocument* pXTextDocument = createDoc("drop_down_form_field_noitem.odt");
+ pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
+
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+
+ // Move the cursor to trigger displaying of the field button.
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ CPPUNIT_ASSERT(m_aFormFieldButton.isEmpty());
+
+ // Do a tile rendering to trigger the button message with a valid text area
+ size_t nCanvasWidth = 1024;
+ size_t nCanvasHeight = 512;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, /*nTilePosX=*/0,
+ /*nTilePosY=*/0, /*nTileWidth=*/10000, /*nTileHeight=*/4000);
+
+ // There is not item specified for the field
+ CPPUNIT_ASSERT(!m_aFormFieldButton.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aFormFieldButton)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ boost::property_tree::ptree aItems = aTree.get_child("params").get_child("items");
+ CPPUNIT_ASSERT_EQUAL(size_t(0), aItems.size());
+
+ OString sSelected( aTree.get_child("params").get_child("selected").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("-1"_ostr, sSelected);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTablePaintInvalidate)
+{
+ // Load a document with a table in it.
+ SwXTextDocument* pXTextDocument = createDoc("table-paint-invalidate.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ // Enter the table.
+ pWrtShell->Down(/*bSelect=*/false);
+ Scheduler::ProcessEventsToIdle();
+ m_nInvalidations = 0;
+
+ // Paint a tile.
+ size_t nCanvasWidth = 256;
+ size_t nCanvasHeight = 256;
+ std::vector<unsigned char> aPixmap(nCanvasWidth * nCanvasHeight * 4, 0);
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nCanvasWidth, nCanvasHeight),
+ Fraction(1.0), Point(), aPixmap.data());
+ pXTextDocument->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, m_aInvalidation.Left(),
+ m_aInvalidation.Top(), /*nTileWidth=*/1000,
+ /*nTileHeight=*/1000);
+ Scheduler::ProcessEventsToIdle();
+
+ // Without the accompanying fix in place, this test would have failed with
+ // - Expected: 0
+ // - Actual : 5
+ // i.e. paint generated an invalidation, which caused a loop.
+ CPPUNIT_ASSERT_EQUAL(0, m_nInvalidations);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTableCommentRemoveCallback)
+{
+ comphelper::LibreOfficeKit::setActive();
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+
+ // Load a document with a comment in a table.
+ SwXTextDocument* pXTextDocument = createDoc("testTableCommentRemoveCallback.odt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ ViewCallback aView;
+
+ // delete all characters
+ comphelper::dispatchCommand(".uno:SelectAll", uno::Sequence<beans::PropertyValue>());
+ Scheduler::ProcessEventsToIdle();
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_DELETE);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_DELETE);
+ Scheduler::ProcessEventsToIdle();
+
+ //check for comment remove callback
+ OString sAction(aView.m_aComment.get_child("action").get_value<std::string>());
+ CPPUNIT_ASSERT_EQUAL("Remove"_ostr, sAction);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSpellOnlineRenderParameter)
+{
+ SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ const SwViewOption* pOpt = pWrtShell->GetViewOptions();
+ bool bSet = pOpt->IsOnlineSpell();
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {".uno:SpellOnline", uno::Any(!bSet)},
+ }));
+ pXTextDocument->initializeForTiledRendering(aPropertyValues);
+ CPPUNIT_ASSERT_EQUAL(!bSet, pOpt->IsOnlineSpell());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testExtTextInputReadOnly)
+{
+ // Create a document with a protected section + a normal paragraph after it.
+ SwXTextDocument* pXTextDocument = createDoc();
+ uno::Reference<text::XTextViewCursorSupplier> xController(
+ pXTextDocument->getCurrentController(), uno::UNO_QUERY);
+ uno::Reference<text::XTextViewCursor> xCursor = xController->getViewCursor();
+ uno::Reference<text::XText> xText = xCursor->getText();
+ uno::Reference<text::XTextContent> xSection(
+ pXTextDocument->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xSectionProps(xSection, uno::UNO_QUERY);
+ xSectionProps->setPropertyValue("IsProtected", uno::Any(true));
+ xText->insertTextContent(xCursor, xSection, /*bAbsorb=*/true);
+
+ // First paragraph is the protected section, is it empty?
+ VclPtr<vcl::Window> pEditWin = pXTextDocument->getDocWindow();
+ CPPUNIT_ASSERT(pEditWin);
+ CPPUNIT_ASSERT(getParagraph(1)->getString().isEmpty());
+
+ // Try to type into the protected section, is it still empty?
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT, "x");
+ SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT_END, "x");
+ Scheduler::ProcessEventsToIdle();
+ // Without the accompanying fix in place, this test would have failed, as it was possible to
+ // type into the protected section.
+ CPPUNIT_ASSERT(getParagraph(1)->getString().isEmpty());
+
+ // Second paragraph is a normal paragraph, is it empty?
+ pWrtShell->Down(/*bSelect=*/false);
+ CPPUNIT_ASSERT(getParagraph(2)->getString().isEmpty());
+
+ // Try to type into the protected section, does it have the typed content?
+ SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT, "x");
+ SfxLokHelper::postExtTextEventAsync(pEditWin, LOK_EXT_TEXTINPUT_END, "x");
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(OUString("x"), getParagraph(2)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletDeleteInvalidation)
+{
+ // Given a document with 3 paragraphs: first 2 is bulleted, the last is not.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->SplitNode();
+ pWrtShell->Up(/*bSelect=*/false);
+ pWrtShell->StartAllAction();
+ pWrtShell->BulletOn();
+ pWrtShell->EndAllAction();
+ pWrtShell->Insert2("a");
+ pWrtShell->SplitNode();
+ pWrtShell->Insert2("b");
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
+ pWrtShell->GetLayout()->getFrameArea());
+ Scheduler::ProcessEventsToIdle();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ m_aInvalidations = tools::Rectangle();
+
+ // When pressing backspace in the last paragraph.
+ pWrtShell->DelLeft();
+
+ // Then the first paragraph should not be invalidated.
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ SwFrame* pPage = pRoot->GetLower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pFirstText = pBody->GetLower();
+ tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
+ CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf155349)
+{
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ Scheduler::ProcessEventsToIdle();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ pWrtShell->Insert2("a");
+ Scheduler::ProcessEventsToIdle();
+ pWrtShell->Insert2("b");
+ m_bFullInvalidateSeen = false;
+ Scheduler::ProcessEventsToIdle();
+ // before fix for tdf#155349 the total area got invalidated when changing one line
+ CPPUNIT_ASSERT(!m_bFullInvalidateSeen);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletNoNumInvalidation)
+{
+ // Given a document with 3 paragraphs: all are bulleted.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->StartAllAction();
+ pWrtShell->BulletOn();
+ pWrtShell->EndAllAction();
+ pWrtShell->Insert2("a");
+ pWrtShell->SplitNode();
+ pWrtShell->Insert2("b");
+ pWrtShell->SplitNode();
+ pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
+ pWrtShell->GetLayout()->getFrameArea());
+ Scheduler::ProcessEventsToIdle();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ m_aInvalidations = tools::Rectangle();
+
+ // When pressing backspace in the last paragraph to turn bullets off.
+ pWrtShell->StartAllAction();
+ pWrtShell->NumOrNoNum(/*bDelete=*/false);
+ pWrtShell->EndAllAction();
+
+ // Then the first paragraph should not be invalidated.
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ SwFrame* pPage = pRoot->GetLower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pFirstText = pBody->GetLower();
+ tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
+ CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testBulletMultiDeleteInvalidation)
+{
+ // Given a document with 5 paragraphs: all are bulleted.
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->StartAllAction();
+ pWrtShell->BulletOn();
+ pWrtShell->EndAllAction();
+ // There is already an initial text node, so type 5 times, but split 4 times.
+ for (int i = 0; i < 4; ++i)
+ {
+ pWrtShell->Insert2("a");
+ pWrtShell->SplitNode();
+ }
+ pWrtShell->Insert2("a");
+ // Go to the end of the 4th para.
+ pWrtShell->Up(/*bSelect=*/false);
+ pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
+ pWrtShell->GetLayout()->getFrameArea());
+ Scheduler::ProcessEventsToIdle();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ m_aInvalidations = tools::Rectangle();
+
+ // When selecting and deleting several bullets: select till the end of the 2nd para and delete.
+ pWrtShell->Up(/*bSelect=*/true, /*nCount=*/2);
+ pWrtShell->DelRight();
+
+ // Then the first paragraph should not be invalidated.
+ SwRootFrame* pRoot = pWrtShell->GetLayout();
+ SwFrame* pPage = pRoot->GetLower();
+ SwFrame* pBody = pPage->GetLower();
+ SwFrame* pFirstText = pBody->GetLower();
+ tools::Rectangle aFirstTextRect = pFirstText->getFrameArea().SVRect();
+ CPPUNIT_ASSERT(!aFirstTextRect.Overlaps(m_aInvalidations));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCondCollCopy)
+{
+ // Given a document with a custom Text Body cond style:
+ SwXTextDocument* pXTextDocument = createDoc("cond-coll-copy.odt");
+ uno::Sequence<beans::PropertyValue> aPropertyValues
+ = { comphelper::makePropertyValue("Style", OUString("Text body")),
+ comphelper::makePropertyValue("FamilyName", OUString("ParagraphStyles")) };
+ dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues);
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->SelAll();
+
+ // When getting the text selection, then make sure it doesn't crash:
+ uno::Reference<datatransfer::XTransferable2> xTransferable(pXTextDocument->getSelection(),
+ css::uno::UNO_QUERY);
+ datatransfer::DataFlavor aFlavor;
+ aFlavor.MimeType = "text/plain;charset=utf-16";
+ aFlavor.DataType = cppu::UnoType<OUString>::get();
+ CPPUNIT_ASSERT(xTransferable->isDataFlavorSupported(aFlavor));
+ // Without the accompanying fix in place, this test would have crashed.
+ xTransferable->getTransferData(aFlavor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlinePortions)
+{
+ // Given a document with 3 portions: before insert redline (foo), the insert redline (ins) and after insert
+ // redline (bar):
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwDocShell* pDocShell = pXTextDocument->GetDocShell();
+ SwView* pView = pDocShell->GetView();
+ pView->SetRedlineAuthor("first");
+ pDocShell->SetView(pView);
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->Insert("foo");
+ pDocShell->SetChangeRecording(true);
+ pWrtShell->Insert("ins");
+ pDocShell->SetChangeRecording(false);
+ pWrtShell->Insert("bar after");
+
+ // When deleting "fooinsbar":
+ pView->SetRedlineAuthor("second");
+ pDocShell->SetView(pView);
+ pWrtShell->SttEndDoc(/*bStt*/true);
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, /*nCount=*/9, /*bBasicCall=*/false);
+ pDocShell->SetChangeRecording(true);
+ pWrtShell->Delete();
+
+ // Then make sure that the portion list is updated, so "bar" can be marked as deleted without
+ // marking " after" as well:
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[1]"_ostr, "portion"_ostr, "foo");
+ assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[2]"_ostr, "portion"_ostr, "ins");
+ // Without the accompanying fix in place, this test would have failed width:
+ // - Expected: bar
+ // - Actual : bar after
+ // i.e. the portion list was outdated, even " after" was marked as deleted.
+ assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[3]"_ostr, "portion"_ostr, "bar");
+ assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[4]"_ostr, "portion"_ostr, " after");
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testContentControl)
+{
+ // Given a document with a content control:
+ SwXTextDocument* pXTextDocument = createDoc();
+ uno::Reference<text::XText> xText = pXTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "test", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ pXTextDocument->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("Alias", uno::Any(OUString("my alias")));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ m_aContentControl.clear();
+
+ // When entering that content control (chars 2-7 are the content control):
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/5, /*bBasicCall=*/false);
+
+ // Then make sure that the callback is emitted:
+ // Without the accompanying fix in place, this test would have failed, no callback was emitted.
+ CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aContentControl)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
+ OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
+ CPPUNIT_ASSERT(!sRectangles.isEmpty());
+ // Without the accompanying fix in place, this test would have failed width:
+ // uncaught exception of type std::exception (or derived).
+ // - No such node (alias)
+ OString sAlias( aTree.get_child("alias").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("my alias"_ostr, sAlias);
+ }
+
+ // And when leaving that content control:
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+
+ // Then make sure that the callback is emitted again:
+ std::stringstream aStream((std::string(m_aContentControl)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("hide"_ostr, sAction);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDropDownContentControl)
+{
+ // Given a document with a dropdown content control:
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "choose an item", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ {
+ uno::Sequence<beans::PropertyValues> aListItems = {
+ {
+ comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))),
+ comphelper::makePropertyValue("Value", uno::Any(OUString("R"))),
+ },
+ {
+ comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))),
+ comphelper::makePropertyValue("Value", uno::Any(OUString("G"))),
+ },
+ {
+ comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))),
+ comphelper::makePropertyValue("Value", uno::Any(OUString("B"))),
+ },
+ };
+ xContentControlProps->setPropertyValue("ListItems", uno::Any(aListItems));
+ }
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ m_aContentControl.clear();
+
+ // When entering that content control:
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
+
+ // Then make sure that the callback is emitted:
+ CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aContentControl)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
+ OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
+ CPPUNIT_ASSERT(!sRectangles.isEmpty());
+ boost::optional<boost::property_tree::ptree&> oItems = aTree.get_child_optional("items");
+ CPPUNIT_ASSERT(oItems);
+ static const std::vector<std::string> vExpected = { "red", "green", "blue" };
+ size_t i = 0;
+ for (const auto& rItem : *oItems)
+ {
+ CPPUNIT_ASSERT_EQUAL(vExpected[i++], rItem.second.get_value<std::string>());
+ }
+ }
+
+ // And when selecting the 2nd item (green):
+ std::map<OUString, OUString> aArguments;
+ aArguments.emplace("type", "drop-down");
+ aArguments.emplace("selected", "1");
+ pXTextDocument->executeContentControlEvent(aArguments);
+
+ // Then make sure that the document is updated accordingly:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: green
+ // - Actual : choose an item
+ // i.e. the document text was not updated.
+ CPPUNIT_ASSERT_EQUAL(OUString("green"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPictureContentControl)
+{
+ // Given a document with a picture content control:
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ uno::Reference<beans::XPropertySet> xTextGraphic(
+ xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ xTextGraphic->setPropertyValue("AnchorType",
+ uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+ uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xTextContent, false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::Any(true));
+ xContentControlProps->setPropertyValue("Picture", uno::Any(true));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ m_aContentControl.clear();
+
+ // When clicking on that content control:
+ pWrtShell->GotoObj(/*bNext=*/true, GotoObjFlags::Any);
+ pWrtShell->EnterSelFrameMode();
+ const SwFrameFormat* pFlyFormat = pWrtShell->GetFlyFrameFormat();
+ const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor();
+ const SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode();
+ const SwTextNode* pTextNode = pAnchorNode->GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ pWrtShell->GotoContentControl(rFormatContentControl);
+
+ // Then make sure that the callback is emitted:
+ // Without the accompanying fix in place, this test would have failed, no callback was emitted.
+ CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
+ std::stringstream aStream((std::string(m_aContentControl)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("change-picture"_ostr, sAction);
+
+ // And when replacing the image:
+ std::map<OUString, OUString> aArguments;
+ aArguments.emplace("type", "picture");
+ OUString aURL = m_directories.getURLFromSrc(u"sw/qa/extras/uiwriter/data/ole2.png");
+ aArguments.emplace("changed", aURL);
+ pXTextDocument->executeContentControlEvent(aArguments);
+
+ // Then make sure that the document is updated accordingly:
+ uno::Reference<drawing::XShape> xShape = getShape(1);
+ auto xGraphic = getProperty<uno::Reference<beans::XPropertySet>>(xShape, "Graphic");
+ // Without the accompanying fix in place, this test would have failed, xGraphic was empty after
+ // executeContentControlEvent().
+ CPPUNIT_ASSERT(xGraphic.is());
+ CPPUNIT_ASSERT_EQUAL(OUString("image/png"), getProperty<OUString>(xGraphic, "MimeType"));
+
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testDateContentControl)
+{
+ // Given a document with a date content control:
+ SwXTextDocument* pXTextDocument = createDoc();
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "choose a date", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("Date", uno::Any(true));
+ xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("YYYY-MM-DD")));
+ xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US")));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ m_aContentControl.clear();
+
+ // When entering that content control:
+ pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
+
+ // Then make sure that the callback is emitted:
+ CPPUNIT_ASSERT(!m_aContentControl.isEmpty());
+ {
+ std::stringstream aStream((std::string(m_aContentControl)));
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+ OString sAction( aTree.get_child("action").get_value<std::string>() );
+ CPPUNIT_ASSERT_EQUAL("show"_ostr, sAction);
+ OString sRectangles( aTree.get_child("rectangles").get_value<std::string>() );
+ CPPUNIT_ASSERT(!sRectangles.isEmpty());
+ boost::optional<boost::property_tree::ptree&> oDate = aTree.get_child_optional("date");
+ CPPUNIT_ASSERT(oDate);
+ }
+
+ // And when selecting a date:
+ std::map<OUString, OUString> aArguments;
+ aArguments.emplace("type", "date");
+ aArguments.emplace("selected", "2022-05-30T00:00:00Z");
+ pXTextDocument->executeContentControlEvent(aArguments);
+
+ // Then make sure that the document is updated accordingly:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2022-05-30
+ // - Actual : choose a date
+ // i.e. the document text was not updated.
+ CPPUNIT_ASSERT_EQUAL(OUString("2022-05-30"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAuthorField)
+{
+ SwXTextDocument* pXTextDocument = createDoc();
+ static constexpr OUString sAuthor(u"Abcd Xyz"_ustr);
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues1(comphelper::InitPropertySequence(
+ {
+ {".uno:Author", uno::Any(sAuthor)},
+ }));
+ pXTextDocument->initializeForTiledRendering(aPropertyValues1);
+
+ auto insertAuthorField = [this]()
+ {
+ uno::Reference<lang::XMultiServiceFactory> const xMSF(mxComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextDocument> const xTD(mxComponent, uno::UNO_QUERY_THROW);
+
+ auto const xText = xTD->getText();
+ auto const xTextCursor = xText->createTextCursor();
+ CPPUNIT_ASSERT(xTextCursor.is());
+
+ xTextCursor->gotoEnd(false);
+
+ uno::Reference<text::XTextField> const xTextField(
+ xMSF->createInstance("com.sun.star.text.textfield.Author"), uno::UNO_QUERY_THROW);
+
+ uno::Reference<beans::XPropertySet> xTextFieldProps(xTextField, uno::UNO_QUERY_THROW);
+ xTextFieldProps->setPropertyValue("FullName", uno::Any(true));
+
+ xText->insertTextContent(xTextCursor, xTextField, false);
+ };
+
+ insertAuthorField();
+ Scheduler::ProcessEventsToIdle();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]"_ostr, "expand"_ostr, sAuthor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSavedAuthorField)
+{
+ SwXTextDocument* pXTextDocument = createDoc("savedauthorfield.odt");
+ static constexpr OUString sAuthor(u"XYZ ABCD"_ustr);
+ uno::Sequence<beans::PropertyValue> aPropertyValues1(comphelper::InitPropertySequence(
+ {
+ {".uno:Author", uno::Any(sAuthor)},
+ }));
+ pXTextDocument->initializeForTiledRendering(aPropertyValues1);
+
+ Scheduler::ProcessEventsToIdle();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]"_ostr, "expand"_ostr, sAuthor);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */