tdf#163340 replace list on paste
[LibreOffice.git] / sw / qa / extras / uiwriter / uiwriter9.cxx
blobf97d5a572d4d1c7af708d8fc4e8e3395492ee596
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <swmodeltestbase.hxx>
11 #include <officecfg/Office/Common.hxx>
12 #include <com/sun/star/document/XEmbeddedObjectSupplier2.hpp>
13 #include <com/sun/star/embed/EmbedStates.hpp>
14 #include <com/sun/star/embed/XEmbeddedObject.hpp>
15 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
16 #include <vcl/scheduler.hxx>
18 #include <com/sun/star/awt/FontWeight.hpp>
19 #include <com/sun/star/awt/FontSlant.hpp>
20 #include <com/sun/star/table/TableBorder2.hpp>
21 #include <com/sun/star/text/XDocumentIndex.hpp>
22 #include <com/sun/star/text/XTextFrame.hpp>
23 #include <com/sun/star/text/XTextTable.hpp>
24 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
25 #include <com/sun/star/text/XPageCursor.hpp>
26 #include <com/sun/star/text/XParagraphCursor.hpp>
27 #include <com/sun/star/view/XSelectionSupplier.hpp>
29 #include <comphelper/lok.hxx>
30 #include <comphelper/propertysequence.hxx>
31 #include <comphelper/propertyvalue.hxx>
32 #include <comphelper/sequence.hxx>
33 #include <comphelper/scopeguard.hxx>
34 #include <comphelper/configuration.hxx>
35 #include <swdtflvr.hxx>
36 #include <o3tl/string_view.hxx>
37 #include <editeng/acorrcfg.hxx>
38 #include <swacorr.hxx>
39 #include <sfx2/linkmgr.hxx>
41 #include <view.hxx>
42 #include <wrtsh.hxx>
43 #include <unotxdoc.hxx>
44 #include <ndtxt.hxx>
45 #include <toxmgr.hxx>
46 #include <IDocumentFieldsAccess.hxx>
47 #include <IDocumentLayoutAccess.hxx>
48 #include <IDocumentRedlineAccess.hxx>
49 #include <IDocumentLinksAdministration.hxx>
50 #include <fmtinfmt.hxx>
51 #include <rootfrm.hxx>
52 #include <svx/svdview.hxx>
53 #include <svx/svdmark.hxx>
55 namespace
57 class SwUiWriterTest9 : public SwModelTestBase
59 public:
60 SwUiWriterTest9()
61 : SwModelTestBase(u"/sw/qa/extras/uiwriter/data/"_ustr)
66 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf158785)
68 // given a document with a hyperlink surrounded by N-dashes (–www.dordt.edu–)
69 createSwDoc("tdf158785_hyperlink.fodt");
70 SwDoc& rDoc = *getSwDoc();
71 SwWrtShell* pWrtShell = rDoc.GetDocShell()->GetWrtShell();
72 CPPUNIT_ASSERT(pWrtShell);
74 // go to the end of the hyperlink
75 pWrtShell->SttEndDoc(/*bStart=*/false);
76 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
77 // get last point that will be part of the hyperlink (current position 1pt wide).
78 Point aLogicL(pWrtShell->GetCharRect().Center());
79 Point aLogicR(aLogicL);
81 // sanity check - we really are right by the hyperlink
82 aLogicL.AdjustX(-1);
83 SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
84 pWrtShell->GetContentAtPos(aLogicL, aContentAtPos);
85 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::InetAttr, aContentAtPos.eContentAtPos);
87 // The test: the position of the N-dash should not indicate hyperlink properties
88 // cursor pos would NOT be considered part of the hyperlink, but increase for good measure...
89 aLogicR.AdjustX(1);
90 pWrtShell->GetContentAtPos(aLogicR, aContentAtPos);
91 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos);
94 * tdf#111969: the beginning of the hyperlink should allow the right-click menu to remove it
96 // move cursor (with no selection) to the start of the hyperlink - after the N-dash
97 pWrtShell->SttEndDoc(/*bStart=*/true);
98 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
99 aLogicL = pWrtShell->GetCharRect().Center();
100 aLogicR = aLogicL;
102 // sanity check - we really are right in front of the hyperlink
103 aLogicL.AdjustX(-1);
104 aContentAtPos = IsAttrAtPos::InetAttr;
105 pWrtShell->GetContentAtPos(aLogicL, aContentAtPos);
106 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos);
107 aLogicR.AdjustX(1);
108 aContentAtPos = IsAttrAtPos::InetAttr;
109 pWrtShell->GetContentAtPos(aLogicR, aContentAtPos);
110 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::InetAttr, aContentAtPos.eContentAtPos);
112 // Remove the hyperlink
113 dispatchCommand(mxComponent, u".uno:RemoveHyperlink"_ustr, {});
115 // The test: was the hyperlink actually removed?
116 aContentAtPos = IsAttrAtPos::InetAttr;
117 pWrtShell->GetContentAtPos(aLogicR, aContentAtPos);
118 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos);
121 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf111969)
123 // given a document with a field surrounded by N-dashes (–date–)
124 createSwDoc("tdf111969_field.fodt");
125 SwDoc& rDoc = *getSwDoc();
126 SwWrtShell* pWrtShell = rDoc.GetDocShell()->GetWrtShell();
127 CPPUNIT_ASSERT(pWrtShell);
129 // go to the end of the field
130 pWrtShell->SttEndDoc(/*bStart=*/false);
131 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
132 // get last point that will be part of the field (current position 1pt wide).
133 Point aLogicL(pWrtShell->GetCharRect().Center());
134 Point aLogicR(aLogicL);
136 // sanity check - we really are at the right edge of the field
137 aLogicR.AdjustX(1);
138 SwContentAtPos aContentAtPos(IsAttrAtPos::Field);
139 pWrtShell->GetContentAtPos(aLogicR, aContentAtPos);
140 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos);
141 aLogicL.AdjustX(-1);
142 aContentAtPos = IsAttrAtPos::Field;
143 pWrtShell->GetContentAtPos(aLogicL, aContentAtPos);
144 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::Field, aContentAtPos.eContentAtPos);
146 // the test: simulate a right-click of a mouse which sets the cursor and then acts on that pos.
147 pWrtShell->SwCursorShell::SetCursor(aLogicL, false, /*Block=*/false, /*FieldInfo=*/true);
148 CPPUNIT_ASSERT(pWrtShell->GetCurField(true));
151 * An edge case at the start of a field - don't start the field menu on the first N-dash
153 // go to the start of the field
154 pWrtShell->SttEndDoc(/*bStart=*/true);
155 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false);
156 // get first point that will be part of the field (current position 1pt wide).
157 aLogicL = pWrtShell->GetCharRect().Center();
158 aLogicR = aLogicL;
160 // sanity check - we really are at the left edge of the field
161 aLogicR.AdjustX(1);
162 aContentAtPos = IsAttrAtPos::Field;
163 pWrtShell->GetContentAtPos(aLogicR, aContentAtPos);
164 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::Field, aContentAtPos.eContentAtPos);
165 aLogicL.AdjustX(-1);
166 aContentAtPos = IsAttrAtPos::Field;
167 pWrtShell->GetContentAtPos(aLogicL, aContentAtPos);
168 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos);
170 // the test: simulate a right-click of a mouse (at the end-edge of the N-dash)
171 // which sets the cursor and then acts on that pos.
172 pWrtShell->SwCursorShell::SetCursor(aLogicL, false, /*Block=*/false, /*FieldInfo=*/true);
173 CPPUNIT_ASSERT(!pWrtShell->GetCurField(true));
176 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf111969B)
178 // given a document with a field surrounded by two N-dashes (––date––)
179 createSwDoc("tdf111969_fieldB.fodt");
180 SwDoc& rDoc = *getSwDoc();
181 SwWrtShell* pWrtShell = rDoc.GetDocShell()->GetWrtShell();
182 CPPUNIT_ASSERT(pWrtShell);
184 // go to the start of the field
185 pWrtShell->SttEndDoc(/*bStart=*/true);
186 pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false);
187 // get first point that will be part of the field (current position 1pt wide).
188 Point aLogicL(pWrtShell->GetCharRect().Center());
189 Point aLogicR(aLogicL);
191 // sanity check - we really are at the left edge of the field
192 aLogicR.AdjustX(1);
193 SwContentAtPos aContentAtPos(IsAttrAtPos::Field);
194 pWrtShell->GetContentAtPos(aLogicR, aContentAtPos);
195 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::Field, aContentAtPos.eContentAtPos);
196 aLogicL.AdjustX(-1);
197 aContentAtPos = IsAttrAtPos::Field;
198 pWrtShell->GetContentAtPos(aLogicL, aContentAtPos);
199 CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos);
201 // the test: simulate a right-click of a mouse (at the end-edge of the second N-dash)
202 // which sets the cursor and then acts on that pos.
203 pWrtShell->SwCursorShell::SetCursor(aLogicL, false, /*Block=*/false, /*FieldInfo=*/true);
204 CPPUNIT_ASSERT(!pWrtShell->GetCurField(true));
207 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159049)
209 // The document contains a shape which has a text with a line break. When copying the text to
210 // clipboard the line break was missing in the RTF flavor of the clipboard.
211 createSwDoc("tdf159049_LineBreakRTFClipboard.fodt");
212 CPPUNIT_ASSERT_EQUAL(1, getShapes());
213 selectShape(1);
215 // Bring shape into text edit mode
216 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
217 CPPUNIT_ASSERT(pTextDoc);
218 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
219 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
220 Scheduler::ProcessEventsToIdle();
221 // Copy text
222 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
223 dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
225 // Deactivate text edit mode ...
226 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_ESCAPE);
227 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_ESCAPE);
228 Scheduler::ProcessEventsToIdle();
229 // ... and deselect shape.
230 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_ESCAPE);
231 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_ESCAPE);
232 Scheduler::ProcessEventsToIdle();
234 // Paste special as RTF
235 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
236 { { "SelectedFormat", uno::Any(static_cast<sal_uInt32>(SotClipboardFormatId::RTF)) } }));
237 dispatchCommand(mxComponent, u".uno:ClipboardFormatItems"_ustr, aArgs);
238 // Without fix Actual was "Abreakhere", the line break \n was missing.
239 CPPUNIT_ASSERT_EQUAL(u"Abreak\nhere"_ustr, getParagraph(1)->getString());
242 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf135083)
244 createSwDoc("tdf135083-simple-text-plus-list.fodt");
246 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
247 dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
249 // Paste special as RTF
250 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
251 { { u"SelectedFormat"_ustr,
252 uno::Any(static_cast<sal_uInt32>(SotClipboardFormatId::RTF)) } }));
253 dispatchCommand(mxComponent, u".uno:ClipboardFormatItems"_ustr, aArgs);
255 auto xLastPara = getParagraph(3);
256 CPPUNIT_ASSERT_EQUAL(u"dolor"_ustr, xLastPara->getString());
257 // Without the fix in place, the last paragraph would loose its settings. ListId would be empty.
258 CPPUNIT_ASSERT(!getProperty<OUString>(xLastPara, u"ListId"_ustr).isEmpty());
261 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testHiddenSectionsAroundPageBreak)
263 createSwDoc("hiddenSectionsAroundPageBreak.fodt");
265 CPPUNIT_ASSERT_EQUAL(1, getPages());
267 auto xModel(mxComponent.queryThrow<frame::XModel>());
268 auto xTextViewCursorSupplier(
269 xModel->getCurrentController().queryThrow<text::XTextViewCursorSupplier>());
270 auto xCursor(xTextViewCursorSupplier->getViewCursor().queryThrow<text::XPageCursor>());
272 // Make sure that the page style is set correctly
273 xCursor->jumpToFirstPage();
274 CPPUNIT_ASSERT_EQUAL(u"Landscape"_ustr, getProperty<OUString>(xCursor, u"PageStyleName"_ustr));
277 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159565)
279 // Given a document with a hidden section in the beginning, additionally containing a frame
280 createSwDoc("FrameInHiddenSection.fodt");
282 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
284 // Check that the selection covers the whole visible text
285 auto xModel(mxComponent.queryThrow<css::frame::XModel>());
286 auto xSelSupplier(xModel->getCurrentController().queryThrow<css::view::XSelectionSupplier>());
287 auto xSelections(xSelSupplier->getSelection().queryThrow<css::container::XIndexAccess>());
288 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelections->getCount());
289 auto xSelection(xSelections->getByIndex(0).queryThrow<css::text::XTextRange>());
291 // Without the fix, this would fail - there was no selection
292 CPPUNIT_ASSERT_EQUAL(u"" SAL_NEWLINE_STRING SAL_NEWLINE_STRING "ipsum"_ustr,
293 xSelection->getString());
296 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159816)
298 createSwDoc();
300 SwDoc* pDoc = getSwDoc();
301 CPPUNIT_ASSERT(pDoc);
302 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
303 CPPUNIT_ASSERT(pWrtShell);
305 // Add 5 empty paragraphs
306 pWrtShell->SplitNode();
307 pWrtShell->SplitNode();
308 pWrtShell->SplitNode();
309 pWrtShell->SplitNode();
310 pWrtShell->SplitNode();
312 // Add a bookmark at the very end
313 IDocumentMarkAccess& rIDMA(*pDoc->getIDocumentMarkAccess());
314 rIDMA.makeMark(*pWrtShell->GetCursor(), u"Mark"_ustr, IDocumentMarkAccess::MarkType::BOOKMARK,
315 sw::mark::InsertMode::New);
317 // Get coordinates of the end point in the document
318 SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
319 SwFrame* pPage = pLayout->Lower();
320 SwFrame* pBody = pPage->GetLower();
321 SwFrame* pLastPara = pBody->GetLower()->GetNext()->GetNext()->GetNext()->GetNext()->GetNext();
322 Point ptTo = pLastPara->getFrameArea().BottomRight();
324 pWrtShell->SelAll();
326 // Drag-n-drop to its own end
327 rtl::Reference<SwTransferable> xTransfer = new SwTransferable(*pWrtShell);
328 // Without the fix, this would crash: either in CopyFlyInFlyImpl (tdf#159813):
329 // Assertion failed: !pCopiedPaM || pCopiedPaM->End()->GetNode() == rRg.aEnd.GetNode()
330 // or in BigPtrArray::operator[] (tdf#159816):
331 // Assertion failed: idx < m_nSize
332 xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true);
335 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf139631)
337 // Unit test for tdf#139631
338 // Test to see if preceding space is cut when cutting a word with track changes (redline) on
339 createSwDoc();
340 SwDoc* pDoc = getSwDoc();
341 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
343 pWrtShell->Insert(u"New World!\""_ustr);
344 // Assert that the string, New World!", is inserted correctly into the document
345 CPPUNIT_ASSERT_EQUAL(u"New World!\""_ustr, getParagraph(1)->getString());
347 // Enable redline
348 dispatchCommand(mxComponent, u".uno:TrackChanges"_ustr, {});
349 CPPUNIT_ASSERT(pDoc->getIDocumentRedlineAccess().IsRedlineOn());
350 // Hide redline changes
351 dispatchCommand(mxComponent, u".uno:ShowTrackedChanges"_ustr, {});
352 CPPUNIT_ASSERT(pWrtShell->GetLayout()->IsHideRedlines());
354 pWrtShell->Left(SwCursorSkipMode::Chars, false, 2, false);
355 // Select and cut "World" from string
356 pWrtShell->Left(SwCursorSkipMode::Chars, true, 5, false);
357 dispatchCommand(mxComponent, u".uno:Cut"_ustr, {});
358 xmlDocUniquePtr pXmlDoc = parseLayoutDump();
359 pXmlDoc = parseLayoutDump();
360 // Verifies that the leading space before "World" was also cut
361 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout/SwParaPortion",
362 "portion", u"New!\"");
364 // Reset to initial string
365 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
366 pXmlDoc = parseLayoutDump();
367 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout/SwParaPortion",
368 "portion", u"New World!\"");
370 pWrtShell->EndPara(false);
371 pWrtShell->Left(SwCursorSkipMode::Chars, false, 1, false);
372 // Replace ! with .
373 pWrtShell->Left(SwCursorSkipMode::Chars, true, 1, false);
374 pWrtShell->Delete();
375 pWrtShell->Insert(u"."_ustr);
376 pXmlDoc = parseLayoutDump();
377 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout/SwParaPortion",
378 "portion", u"New World.\"");
380 pWrtShell->Left(SwCursorSkipMode::Chars, false, 1, false);
381 // Select and cut "World" from string
382 pWrtShell->Left(SwCursorSkipMode::Chars, true, 5, false);
383 dispatchCommand(mxComponent, u".uno:Cut"_ustr, {});
385 pXmlDoc = parseLayoutDump();
386 // Without the test in place, the leading space before "World" is not also cut.
387 // Expected: New."
388 // Actual: New ."
389 assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout/SwParaPortion",
390 "portion", u"New.\"");
393 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf151710)
395 createSwDoc();
396 SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
397 CPPUNIT_ASSERT(pTextDoc);
399 // Check that the particular setting is turned on by default
400 const SwViewOption* pVwOpt = pTextDoc->GetDocShell()->GetWrtShell()->GetViewOptions();
401 CPPUNIT_ASSERT(pVwOpt);
402 CPPUNIT_ASSERT(pVwOpt->IsEncloseWithCharactersOn());
404 // Localized quotation marks
405 SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
406 CPPUNIT_ASSERT(pACorr);
407 LanguageType eLang = Application::GetSettings().GetLanguageTag().getLanguageType();
408 OUString sStartSingleQuote{ pACorr->GetQuote('\'', true, eLang) };
409 OUString sEndSingleQuote{ pACorr->GetQuote('\'', false, eLang) };
410 OUString sStartDoubleQuote{ pACorr->GetQuote('\"', true, eLang) };
411 OUString sEndDoubleQuote{ pACorr->GetQuote('\"', false, eLang) };
413 // Insert some text to work with
414 uno::Sequence<beans::PropertyValue> aArgsInsert(
415 comphelper::InitPropertySequence({ { "Text", uno::Any(u"abcd"_ustr) } }));
416 dispatchCommand(mxComponent, u".uno:InsertText"_ustr, aArgsInsert);
417 CPPUNIT_ASSERT_EQUAL(u"abcd"_ustr, pTextDoc->getText()->getString());
419 // Successfully enclose the text; afterwards the selection should exist with the new
420 // enclosed text
421 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
422 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '(', 0);
423 Scheduler::ProcessEventsToIdle();
424 CPPUNIT_ASSERT_EQUAL(u"(abcd)"_ustr, pTextDoc->getText()->getString());
426 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '[', 0);
427 Scheduler::ProcessEventsToIdle();
428 CPPUNIT_ASSERT_EQUAL(u"[(abcd)]"_ustr, pTextDoc->getText()->getString());
430 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '{', 0);
431 Scheduler::ProcessEventsToIdle();
432 CPPUNIT_ASSERT_EQUAL(u"{[(abcd)]}"_ustr, pTextDoc->getText()->getString());
434 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\'', 0);
435 Scheduler::ProcessEventsToIdle();
436 CPPUNIT_ASSERT_EQUAL(OUString(sStartSingleQuote + "{[(abcd)]}" + sEndSingleQuote),
437 pTextDoc->getText()->getString());
439 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\"', 0);
440 Scheduler::ProcessEventsToIdle();
441 CPPUNIT_ASSERT_EQUAL(OUString(sStartDoubleQuote + sStartSingleQuote + "{[(abcd)]}"
442 + sEndSingleQuote + sEndDoubleQuote),
443 pTextDoc->getText()->getString());
445 // Disable the setting and check that enclosing doesn't happen anymore
446 const_cast<SwViewOption*>(pVwOpt)->SetEncloseWithCharactersOn(false);
447 CPPUNIT_ASSERT(!pVwOpt->IsEncloseWithCharactersOn());
449 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '(', 0);
450 Scheduler::ProcessEventsToIdle();
451 CPPUNIT_ASSERT_EQUAL(u"("_ustr, pTextDoc->getText()->getString());
453 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
454 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '[', 0);
455 Scheduler::ProcessEventsToIdle();
456 CPPUNIT_ASSERT_EQUAL(u"["_ustr, pTextDoc->getText()->getString());
458 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
459 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '{', 0);
460 Scheduler::ProcessEventsToIdle();
461 CPPUNIT_ASSERT_EQUAL(u"{"_ustr, pTextDoc->getText()->getString());
463 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
464 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\'', 0);
465 Scheduler::ProcessEventsToIdle();
466 CPPUNIT_ASSERT_EQUAL(sStartSingleQuote, pTextDoc->getText()->getString());
468 dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
469 pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\"', 0);
470 Scheduler::ProcessEventsToIdle();
471 CPPUNIT_ASSERT_EQUAL(sStartDoubleQuote, pTextDoc->getText()->getString());
474 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159054_disableOutlineNumbering)
476 createSwDoc("tdf159054_disableOutlineNumbering.docx");
477 SwDoc* pDoc = getSwDoc();
478 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
480 const uno::Reference<text::XTextRange> xPara1 = getParagraph(1, u"Heading A"_ustr);
481 const uno::Reference<text::XTextRange> xPara2 = getParagraph(2, u"Heading B"_ustr);
482 const uno::Reference<text::XTextRange> xPara3 = getParagraph(3, u"Heading C"_ustr);
484 CPPUNIT_ASSERT_EQUAL(u"A."_ustr, getProperty<OUString>(xPara1, u"ListLabelString"_ustr));
485 CPPUNIT_ASSERT_EQUAL(u"B."_ustr, getProperty<OUString>(xPara2, u"ListLabelString"_ustr));
486 CPPUNIT_ASSERT_EQUAL(u"C."_ustr, getProperty<OUString>(xPara3, u"ListLabelString"_ustr));
488 // select (at least parts) of the first two paragraphs
489 pWrtShell->Down(/*bSelect=*/true, /*nCount=*/1, /*bBasicCall=*/true);
491 // on the selection, simulate pressing the toolbar button to toggle OFF numbering
492 dispatchCommand(mxComponent, u".uno:DefaultNumbering"_ustr, {});
494 // the selected paragraphs should definitely have the list label removed
495 CPPUNIT_ASSERT_EQUAL(u""_ustr, getProperty<OUString>(xPara1, u"ListLabelString"_ustr));
496 CPPUNIT_ASSERT_EQUAL(u""_ustr, getProperty<OUString>(xPara2, u"ListLabelString"_ustr));
497 // the third paragraph must retain the existing numbering format
498 CPPUNIT_ASSERT_EQUAL(u"A."_ustr, getProperty<OUString>(xPara3, u"ListLabelString"_ustr));
500 // on the selection, simulate pressing the toolbar button to toggle ON numbering again
501 dispatchCommand(mxComponent, u".uno:DefaultNumbering"_ustr, {});
503 // the outline numbering format must be re-applied to the first two paragraphs
504 CPPUNIT_ASSERT_EQUAL(u"A."_ustr, getProperty<OUString>(xPara1, u"ListLabelString"_ustr));
505 CPPUNIT_ASSERT_EQUAL(u"B."_ustr, getProperty<OUString>(xPara2, u"ListLabelString"_ustr));
506 CPPUNIT_ASSERT_EQUAL(u"C."_ustr, getProperty<OUString>(xPara3, u"ListLabelString"_ustr));
508 // on the selection, simulate a right click - list - No list
509 dispatchCommand(mxComponent, u".uno:RemoveBullets"_ustr, {});
511 // the selected paragraphs should definitely have the list label removed
512 CPPUNIT_ASSERT_EQUAL(u""_ustr, getProperty<OUString>(xPara1, u"ListLabelString"_ustr));
513 CPPUNIT_ASSERT_EQUAL(u""_ustr, getProperty<OUString>(xPara2, u"ListLabelString"_ustr));
514 CPPUNIT_ASSERT_EQUAL(u"A."_ustr, getProperty<OUString>(xPara3, u"ListLabelString"_ustr));
517 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf158375_dde_disable)
519 std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
520 comphelper::ConfigurationChanges::create());
521 officecfg::Office::Common::Security::Scripting::DisableActiveContent::set(true, pBatch);
522 pBatch->commit();
523 comphelper::ScopeGuard g([] {
524 std::shared_ptr<comphelper::ConfigurationChanges> _pBatch(
525 comphelper::ConfigurationChanges::create());
526 officecfg::Office::Common::Security::Scripting::DisableActiveContent::set(false, _pBatch);
527 _pBatch->commit();
530 createSwDoc();
531 SwDoc* pDoc = getSwDoc();
533 // force the AppName to enable DDE, it is not there for test runs
534 Application::SetAppName(u"soffice"_ustr);
536 // temp copy for the file that will be used as a reference for DDE link
537 // this file includes a section named "Section1" with text inside
538 createTempCopy(u"tdf158375_dde_reference.fodt");
540 comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer
541 = getSwDocShell()->getEmbeddedObjectContainer();
542 rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true);
544 // create a section with DDE link
545 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
546 uno::Reference<beans::XPropertySet> xTextSectionProps(
547 xFactory->createInstance(u"com.sun.star.text.TextSection"_ustr), uno::UNO_QUERY);
549 uno::Sequence<OUString> aNames{ u"DDECommandFile"_ustr, u"DDECommandType"_ustr,
550 u"DDECommandElement"_ustr, u"IsAutomaticUpdate"_ustr,
551 u"IsProtected"_ustr };
552 uno::Sequence<uno::Any> aValues{ uno::Any(u"soffice"_ustr), uno::Any(maTempFile.GetURL()),
553 uno::Any(u"Section1"_ustr), uno::Any(true), uno::Any(true) };
554 uno::Reference<beans::XMultiPropertySet> rMultiPropSet(xTextSectionProps, uno::UNO_QUERY);
555 rMultiPropSet->setPropertyValues(aNames, aValues);
557 // insert the TextSection with DDE link
558 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
559 uno::Reference<text::XTextRange> xTextRange = xTextDocument->getText();
560 uno::Reference<text::XText> xText = xTextRange->getText();
561 uno::Reference<text::XParagraphCursor> xCursor(xText->createTextCursor(), uno::UNO_QUERY);
562 xText->insertTextContent(
563 xCursor, uno::Reference<text::XTextContent>(xTextSectionProps, uno::UNO_QUERY), false);
565 CPPUNIT_ASSERT_EQUAL(
566 size_t(1), pDoc->getIDocumentLinksAdministration().GetLinkManager().GetLinks().size());
568 pDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks(false, false, nullptr,
569 u""_ustr);
571 uno::Reference<text::XTextSectionsSupplier> xTextSectionsSupplier(mxComponent, uno::UNO_QUERY);
572 uno::Reference<container::XIndexAccess> xSections(xTextSectionsSupplier->getTextSections(),
573 uno::UNO_QUERY);
574 uno::Reference<text::XTextSection> xSection(xSections->getByIndex(0), uno::UNO_QUERY);
576 // make sure there's no text in the section after UpdateAllLinks, since
577 // DisableActiveContent disables DDE links.
578 CPPUNIT_ASSERT_EQUAL(u""_ustr, xSection->getAnchor()->getString());
581 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf158375_ole_object_disable)
583 std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
584 comphelper::ConfigurationChanges::create());
585 officecfg::Office::Common::Security::Scripting::DisableActiveContent::set(true, pBatch);
586 pBatch->commit();
587 comphelper::ScopeGuard g([] {
588 std::shared_ptr<comphelper::ConfigurationChanges> _pBatch(
589 comphelper::ConfigurationChanges::create());
590 officecfg::Office::Common::Security::Scripting::DisableActiveContent::set(false, _pBatch);
591 _pBatch->commit();
594 // Enable LOK mode, otherwise OCommonEmbeddedObject::SwitchStateTo_Impl() will throw when it
595 // finds out that the test runs headless.
596 comphelper::LibreOfficeKit::setActive();
598 // Load a document with a Draw doc in it.
599 createSwDoc("ole-save-while-edit.odt");
600 SwDoc* pDoc = getSwDoc();
601 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
603 selectShape(1);
605 // attempt to edit the OLE object.
606 pWrtShell->LaunchOLEObj();
608 // it shouldn't switch because the current configuration
609 // (DisableActiveContent) prohibits OLE objects changing to states other
610 // then LOADED
611 auto xShape = getShape(1);
612 uno::Reference<document::XEmbeddedObjectSupplier2> xEmbedSupplier(xShape, uno::UNO_QUERY);
613 auto xEmbeddedObj = xEmbedSupplier->getExtendedControlOverEmbeddedObject();
614 CPPUNIT_ASSERT_EQUAL(embed::EmbedStates::LOADED, xEmbeddedObj->getCurrentState());
616 // Dispose the document while LOK is still active to avoid leaks.
617 mxComponent->dispose();
618 mxComponent.clear();
619 comphelper::LibreOfficeKit::setActive(false);
622 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf146190)
624 // Given a document with a number rule at the start of a paragraph and two drawing objects:
625 createSwDoc("tdf146190.odt");
626 SwXTextDocument* pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
627 SwDocShell* pDocShell = pXTextDocument->GetDocShell();
628 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
630 const SdrMarkList& rMrkList = pWrtShell->GetDrawView()->GetMarkedObjectList();
632 // Assert the current cursor position has a number rule and is at the start of a paragraph:
633 pWrtShell->SttEndDoc(/*bStt=*/true);
634 CPPUNIT_ASSERT(pWrtShell->GetNumRuleAtCurrCursorPos());
635 CPPUNIT_ASSERT(pWrtShell->IsSttOfPara());
637 // Then go to "Shape 1" drawing object using the GotoDrawingObject function:
638 pWrtShell->GotoDrawingObject(u"Shape 1");
639 CPPUNIT_ASSERT_EQUAL(u"Shape 1"_ustr, rMrkList.GetMark(0)->GetMarkedSdrObj()->GetName());
641 // Move to the next drawing object by Tab key press:
642 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
643 Scheduler::ProcessEventsToIdle();
644 // Without the fix in place, this test would have failed with:
645 // equality assertion failed
646 // - Expected: Shape 2
647 // - Actual : Shape 1
648 // i.e. Tab did not move to the next drawing object
649 CPPUNIT_ASSERT_EQUAL(u"Shape 2"_ustr, rMrkList.GetMark(0)->GetMarkedSdrObj()->GetName());
651 // Tab key press should now select 'Shape 1':
652 pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
653 Scheduler::ProcessEventsToIdle();
654 CPPUNIT_ASSERT_EQUAL(u"Shape 1"_ustr, rMrkList.GetMark(0)->GetMarkedSdrObj()->GetName());
657 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf160898)
659 // Given a document with a 1-cell table in another 1-cell table:
660 createSwDoc("table-in-table.fodt");
661 SwXTextDocument* pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
662 SwDocShell* pDocShell = pXTextDocument->GetDocShell();
663 SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
665 // Move to the normally hidden paragraph inside the outer table cell, following the inner table
666 pWrtShell->Down(false, 2);
667 // Without the fix, this would crash:
668 pWrtShell->SelAll();
671 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf161172)
673 // Given a paragraph manually made a member of a list:
674 createSwDoc("tdf161172.fodt");
675 auto para = getParagraph(1);
677 // Check initial state: the first paragraph has "No_list" para style, "Num_1" numbering style,
678 // numbering level 0, and "Num1_lvl1_1" numbering label.
679 CPPUNIT_ASSERT_EQUAL(u"No_list"_ustr, getProperty<OUString>(para, u"ParaStyleName"_ustr));
680 CPPUNIT_ASSERT_EQUAL(u"Num_1"_ustr, getProperty<OUString>(para, u"NumberingStyleName"_ustr));
681 CPPUNIT_ASSERT_EQUAL(u"Num1_lvl1_1"_ustr, getProperty<OUString>(para, u"ListLabelString"_ustr));
682 CPPUNIT_ASSERT_EQUAL(sal_Int16(0), getProperty<sal_Int16>(para, u"NumberingLevel"_ustr));
684 // Assign "Num_1_lvl2" paragraph style to the first paragraph. The style is associated with
685 // "Num_1" numbering style, level 1.
686 dispatchCommand(mxComponent, u".uno:StyleApply"_ustr,
687 { comphelper::makePropertyValue(u"FamilyName"_ustr, u"ParagraphStyles"_ustr),
688 comphelper::makePropertyValue(u"Style"_ustr, u"Num_1_lvl2"_ustr) });
690 // Check that the respective properties got correctly applied
691 CPPUNIT_ASSERT_EQUAL(u"Num_1_lvl2"_ustr, getProperty<OUString>(para, u"ParaStyleName"_ustr));
692 CPPUNIT_ASSERT_EQUAL(u"Num_1"_ustr, getProperty<OUString>(para, u"NumberingStyleName"_ustr));
693 CPPUNIT_ASSERT_EQUAL(u"Num1_lvl2_1"_ustr, getProperty<OUString>(para, u"ListLabelString"_ustr));
694 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), getProperty<sal_Int16>(para, u"NumberingLevel"_ustr));
696 // Undo
697 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
699 // Check that the numbering properties got correctly restored
700 CPPUNIT_ASSERT_EQUAL(u"No_list"_ustr, getProperty<OUString>(para, u"ParaStyleName"_ustr));
701 CPPUNIT_ASSERT_EQUAL(u"Num_1"_ustr, getProperty<OUString>(para, u"NumberingStyleName"_ustr));
702 // Without the fix, this would fail with
703 // - Expected: Num1_lvl1_1
704 // - Actual : Num1_lvl2_1
705 CPPUNIT_ASSERT_EQUAL(u"Num1_lvl1_1"_ustr, getProperty<OUString>(para, u"ListLabelString"_ustr));
706 // Without the fix, this would fail with
707 // - Expected: 0
708 // - Actual : 1
709 CPPUNIT_ASSERT_EQUAL(sal_Int16(0), getProperty<sal_Int16>(para, u"NumberingLevel"_ustr));
712 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf144752)
714 // Undoing/redoing a replacement must select the new text
715 createSwDoc();
716 SwXTextDocument* pDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
717 CPPUNIT_ASSERT(pDoc);
718 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
719 CPPUNIT_ASSERT(pWrtShell);
721 emulateTyping(*pDoc, u"Some Text");
722 CPPUNIT_ASSERT(!pWrtShell->HasSelection());
723 // Select "Text", and replace with "Word"
724 pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect*/ true, 4, /*bBasicCall*/ false);
725 pWrtShell->Replace(u"Word"_ustr, false);
726 pWrtShell->EndOfSection();
727 CPPUNIT_ASSERT(!pWrtShell->HasSelection());
729 // Undo and check, that the "Text" is selected
730 dispatchCommand(mxComponent, u".uno:Undo"_ustr, {});
731 // Without the fix, this would fail
732 CPPUNIT_ASSERT(pWrtShell->HasSelection());
733 CPPUNIT_ASSERT_EQUAL(u"Text"_ustr, pWrtShell->GetSelText());
735 // Redo and check, that the "Word" is selected
736 dispatchCommand(mxComponent, u".uno:Redo"_ustr, {});
737 CPPUNIT_ASSERT(pWrtShell->HasSelection());
738 CPPUNIT_ASSERT_EQUAL(u"Word"_ustr, pWrtShell->GetSelText());
741 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf162326_Pargraph)
743 createSwDoc("tdf162326.odt");
744 SwXTextDocument* pDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
745 CPPUNIT_ASSERT(pDoc);
746 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
747 CPPUNIT_ASSERT(pWrtShell);
749 CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD,
750 getProperty<float>(getRun(getParagraph(1), 1), u"CharWeight"_ustr));
751 CPPUNIT_ASSERT_EQUAL(
752 awt::FontSlant_ITALIC,
753 getProperty<awt::FontSlant>(getRun(getParagraph(2), 2), u"CharPosture"_ustr));
754 CPPUNIT_ASSERT_EQUAL(short(1),
755 getProperty<short>(getRun(getParagraph(3), 2), u"CharUnderline"_ustr));
757 pWrtShell->Down(/*bSelect=*/true, 3);
759 dispatchCommand(mxComponent, u".uno:StyleApply"_ustr,
760 { comphelper::makePropertyValue(u"FamilyName"_ustr, u"ParagraphStyles"_ustr),
761 comphelper::makePropertyValue(u"Style"_ustr, u"Footnote"_ustr),
762 comphelper::makePropertyValue(u"KeyModifier"_ustr, uno::Any(KEY_MOD1)) });
764 CPPUNIT_ASSERT_EQUAL(awt::FontWeight::NORMAL,
765 getProperty<float>(getRun(getParagraph(1), 1), u"CharWeight"_ustr));
766 CPPUNIT_ASSERT_THROW(getRun(getParagraph(2), 2), css::container::NoSuchElementException);
767 CPPUNIT_ASSERT_THROW(getRun(getParagraph(3), 2), css::container::NoSuchElementException);
770 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf162326_Character)
772 createSwDoc("tdf162326.odt");
773 SwXTextDocument* pDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
774 CPPUNIT_ASSERT(pDoc);
775 SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
776 CPPUNIT_ASSERT(pWrtShell);
778 CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD,
779 getProperty<float>(getRun(getParagraph(1), 1), u"CharWeight"_ustr));
780 CPPUNIT_ASSERT_EQUAL(
781 awt::FontSlant_ITALIC,
782 getProperty<awt::FontSlant>(getRun(getParagraph(2), 2), u"CharPosture"_ustr));
783 CPPUNIT_ASSERT_EQUAL(short(1),
784 getProperty<short>(getRun(getParagraph(3), 2), u"CharUnderline"_ustr));
786 pWrtShell->Down(/*bSelect=*/true, 3);
788 //add Ctrl/MOD_1
789 dispatchCommand(mxComponent, u".uno:StyleApply"_ustr,
790 { comphelper::makePropertyValue(u"FamilyName"_ustr, u"CharacterStyles"_ustr),
791 comphelper::makePropertyValue(u"Style"_ustr, u"Definition"_ustr),
792 comphelper::makePropertyValue(u"KeyModifier"_ustr, uno::Any(KEY_MOD1)) });
794 CPPUNIT_ASSERT_EQUAL(awt::FontWeight::NORMAL,
795 getProperty<float>(getRun(getParagraph(1), 1), u"CharWeight"_ustr));
796 CPPUNIT_ASSERT_THROW(getRun(getParagraph(2), 2), css::container::NoSuchElementException);
797 //last runs are not changed because the selection ends at the beginning of that paragraph
798 CPPUNIT_ASSERT_EQUAL(short(1),
799 getProperty<short>(getRun(getParagraph(3), 2), u"CharUnderline"_ustr));
801 CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf163340)
803 createSwDoc("tdf163340.odt");
804 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
805 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
806 uno::Reference<text::XText> xText = xTextDocument->getText();
807 uno::Reference<view::XSelectionSupplier> xSelSupplier(xModel->getCurrentController(),
808 uno::UNO_QUERY_THROW);
809 uno::Reference<text::XParagraphCursor> xParaCursor(xTextDocument->getText()->createTextCursor(),
810 uno::UNO_QUERY);
812 for (int i = 0; i < 14; i++)
813 xParaCursor->gotoNextParagraph(false);
814 xParaCursor->gotoEndOfParagraph(true);
815 xSelSupplier->select(uno::Any(xParaCursor));
817 CPPUNIT_ASSERT_EQUAL(u"A."_ustr, getProperty<OUString>(xParaCursor, u"ListLabelString"_ustr));
818 dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
820 xParaCursor = uno::Reference<text::XParagraphCursor>(xText->createTextCursor(), uno::UNO_QUERY);
821 for (int i = 0; i < 3; i++)
822 xParaCursor->gotoNextParagraph(false);
823 xParaCursor->gotoEndOfParagraph(true);
824 CPPUNIT_ASSERT_EQUAL(u"1."_ustr, getProperty<OUString>(xParaCursor, u"ListLabelString"_ustr));
825 xSelSupplier->select(uno::Any(xParaCursor));
826 dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});
827 CPPUNIT_ASSERT_EQUAL(u"A."_ustr, getProperty<OUString>(xParaCursor, u"ListLabelString"_ustr));
830 } // end of anonymous namespace
831 CPPUNIT_PLUGIN_IMPLEMENT();
833 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */