sw/qa/extras/layout/data/tdf150717.odt |binary sw/qa/extras/layout/data/tdf150790.fodt | 9 +++ sw/qa/extras/layout/layout2.cxx | 31 ++++++++++++ sw/qa/extras/mailmerge/mailmerge.cxx | 17 +++++- sw/qa/extras/ooxmlexport/ooxmlexport3.cxx | 3 + sw/qa/extras/ooxmlimport/ooxmlimport.cxx | 28 +++++++++++ sw/source/core/access/accportions.cxx | 3 - sw/source/core/inc/scriptinfo.hxx | 6 +- sw/source/core/text/itrform2.cxx | 33 ++----------- sw/source/core/text/porlay.cxx | 62 ++++++++++++------------- sw/source/core/text/porrst.cxx | 69 +++++++++++++++++++++++----- sw/source/core/text/porrst.hxx | 23 ++++++++- sw/uiconfig/swriter/ui/optformataidspage.ui | 6 +- 13 files changed, 207 insertions(+), 83 deletions(-)
New commits: commit 5e71ec6aa0d4f9971f5e2b6f7091d18668c2f0b9 Author: László Németh <nem...@numbertext.org> AuthorDate: Fri Sep 2 19:50:14 2022 +0200 Commit: László Németh <nem...@numbertext.org> CommitDate: Fri Sep 16 11:32:55 2022 +0200 tdf#150790 tdf#150791 tdf#150947 tdf#150956 sw, a11y: fix visible bookmarks Fix bookmark visibility (see Tools -> Options -> LibreOffice Writer -> Formatting Aids -> Bookmarks): – tdf#150791 Paint I-beam symbol (⌶) for point bookmarks instead of a vertical line (which was overlapped with visible page margin at beginning of the paragraphs, also it was very similar to the NBSP and narrow NBSP shading). Modify associated dialog window label in optformataidspage.ui according to this. – tdf#150790 Neighboring bookmark ranges are shown as ][ instead of a single vertical line (which could not be distinguished from point bookmark symbol and overlapping page border). Note: this has been implemented for bookmarks with metadata colors since commit 412e0ab26618c38f548c340f85ef63bbe73ef6b2 "tdf#150717 sw RDF metadata: add custom color bookmark boundary marks". – Multiple bookmarks at the same character position are visible now, e.g. instead of a single |, neighboring bookmarks with point bookmarks are shown as "] ⌶ [" or "] ] ⌶ ⌶ [ [" etc. – tdf#150956 Do not show hidden _Toc and _Ref bookmarks imported from OOXML, like MSO does. Add bookmark accessibility and layout dump: – Add SwBookmarkPortion::HandlePortion() to return with bookmark names and types, e.g. for ] ⌶ [ as "#Name1 Bookmark End#Name2 Bookmark#Name3 Bookmark Start" (where "Name1"–"Name3" are names of the bookmarks, while "Bookmark", "End" and "Start" are localized versions of these strings), allowing XML layout dump of bookmark data. – tdf#150947 Extend SwAccessiblePortionData::Special() to forward the previous HandlePortion() bookmark data to screen readers depending on bookmark visibility (i.e. SwViewOption::IsShowBookmarks()). – Add unit tests based on the extended XML layout dump, also adjust existing unit tests. More visibility fixes: – multiple start marks of range bookmarks positioned before the character position, multiple end marks of range bookmarks positioned after the character position, i.e. single type boundary marks are there outside of the bookmark text some |text| here [[ ]] – neighboring end and start marks are centered around the character position: |text1|text2| [ ]] [[ ] – marks of the same character position are sorted based on their type and metadata color: – mark order: ], ⌶, [, e.g. ] ] ⌶ ⌶ [ [ [ – color order: reversed at bookmark end, e.g. [c1 [c2 [c3 ... c3] c2] c1] Follow-up to commit 412e0ab26618c38f548c340f85ef63bbe73ef6b2 "tdf#150717 sw RDF metadata: add custom color bookmark boundary marks" and commit 4ce8120f1e53f7b81e653b01d141643013bc69ab "tdf#45589 sw: create and paint text portions for bookmarks". Change-Id: Ia53903efbc44a531338b8615d9812d6da270e39f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139817 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/sw/qa/extras/layout/data/tdf150717.odt b/sw/qa/extras/layout/data/tdf150717.odt new file mode 100644 index 000000000000..f3a7c4e7d586 Binary files /dev/null and b/sw/qa/extras/layout/data/tdf150717.odt differ diff --git a/sw/qa/extras/layout/data/tdf150790.fodt b/sw/qa/extras/layout/data/tdf150790.fodt new file mode 100644 index 000000000000..33ff6fa1054e --- /dev/null +++ b/sw/qa/extras/layout/data/tdf150790.fodt @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<office:document xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooow="http://openoffice.org/2004/writer" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:body> + <office:text> + <text:p><text:bookmark text:name="Bookmark 1"/><text:span text:style-name="T1">point b</text:span>ookmark</text:p> + <text:p><text:bookmark-start text:name="Bookmark 2"/>neigh<text:bookmark-start text:name="Bookmark 3"/><text:bookmark-end text:name="Bookmark 2"/>boring range bookmarks<text:bookmark-end text:name="Bookmark 3"/></text:p> + </office:text> + </office:body> +</office:document> diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx index a9296b900f48..208bdf908d19 100644 --- a/sw/qa/extras/layout/layout2.cxx +++ b/sw/qa/extras/layout/layout2.cxx @@ -403,6 +403,37 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testtdf138951) // Without the fix the anchor differs, and the frame outside of the shape } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf150717) +{ + createSwDoc(DATA_DIRECTORY, "tdf150717.odt"); + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // check bookmark colors defined in metadata + assertXPath(pXmlDoc, "/root/page/body/txt/Special[1]", "rText", "#Bookmark1 Bookmark Start"); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[2]", "rText", "#Bookmark2 Bookmark Start"); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[3]", "rText", + "#Bookmark2 Bookmark End#Bookmark1 Bookmark End"); + // full text, if bookmarks are visible + assertXPath(pXmlDoc, "/root/page/body/txt/LineBreak", "Line", + "Lorem #Bookmark1 Bookmark Startipsum dolor et #Bookmark2 Bookmark Start" + "ames#Bookmark2 Bookmark End#Bookmark1 Bookmark End."); +} + +CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf150790) +{ + createSwDoc(DATA_DIRECTORY, "tdf150790.fodt"); + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // point bookmark is shown as I-beam (only its text dump is |, as before on the screen) + assertXPath(pXmlDoc, "/root/page/body/txt[1]/Special", "rText", "#Bookmark 1 Bookmark"); + // single start bookmark + assertXPath(pXmlDoc, "/root/page/body/txt[2]/Special[1]", "rText", + "#Bookmark 2 Bookmark Start"); + // single end bookmark + assertXPath(pXmlDoc, "/root/page/body/txt[2]/Special[3]", "rText", "#Bookmark 3 Bookmark End"); + // This was |, as before the point bookmark (neighboring end and start bookmarks) + assertXPath(pXmlDoc, "/root/page/body/txt[2]/Special[2]", "rText", + "#Bookmark 2 Bookmark End#Bookmark 3 Bookmark Start"); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineNumberInNumbering) { SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf42748.fodt"); diff --git a/sw/qa/extras/mailmerge/mailmerge.cxx b/sw/qa/extras/mailmerge/mailmerge.cxx index 9daf733fe49f..fbeded40b94b 100644 --- a/sw/qa/extras/mailmerge/mailmerge.cxx +++ b/sw/qa/extras/mailmerge/mailmerge.cxx @@ -607,18 +607,27 @@ DECLARE_SHELL_MAILMERGE_TEST(testBookmarkCondition, "bookmarkcondition.fodt", "b assertXPath(pLayout, "/root/page", 7); assertXPath(pLayout, "/root/page[1]/body/section", 1); assertXPath(pLayout, "/root/page[1]/body/section[1]/txt[1]/LineBreak", "Line", u"In den Bergen war es anstrengend."); - assertXPath(pLayout, "/root/page[1]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war anstrengend . "); + assertXPath(pLayout, "/root/page[1]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war #S_Berge Bookmark Startanstrengend #S_Berge Bookmark End. "); assertXPath(pLayout, "/root/page[3]/body/section", 1); assertXPath(pLayout, "/root/page[3]/body/section[1]/txt[1]/LineBreak", "Line", u"In Barcelona war es schön."); - assertXPath(pLayout, "/root/page[3]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war schön . "); + assertXPath(pLayout, "/root/page[3]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war #S_Barcelona Bookmark Startschön #S_Barcelona Bookmark End. "); assertXPath(pLayout, "/root/page[5]/body/section", 1); assertXPath(pLayout, "/root/page[5]/body/section[1]/txt[1]/LineBreak", "Line", "In Paris war es erlebnisreich."); - assertXPath(pLayout, "/root/page[5]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war erlebnisreich . "); + assertXPath(pLayout, "/root/page[5]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war #S_Paris Bookmark Starterlebnisreich #S_Paris Bookmark End. "); assertXPath(pLayout, "/root/page[7]/body/section", 3); assertXPath(pLayout, "/root/page[7]/body/section[1]/txt[1]/LineBreak", "Line", u"In den Bergen war es anstrengend."); assertXPath(pLayout, "/root/page[7]/body/section[2]/txt[1]/LineBreak", "Line", u"In Barcelona war es schön."); assertXPath(pLayout, "/root/page[7]/body/section[3]/txt[1]/LineBreak", "Line", u"In Paris war es erlebnisreich."); - assertXPath(pLayout, "/root/page[7]/body/txt[5]/LineBreak", "Line", u"Mein Urlaub war anstrengend schön erlebnisreich . "); + OUString sMerge(getXPath(pLayout, "/root/page[7]/body/txt[5]/LineBreak", "Line")); + // bookmark names contain time stamps, e.g. + // "u"Mein Urlaub war #S_BergeMailMergeMark2022-09-15T15:55:17Z7 Bookmark Startanstrengend " + // "#S_BergeMailMergeMark2022-09-15T15:55:17Z7 Bookmark End#S_BarcelonaMailMergeMark2022-09-15T15:55:17Z8 Bookmark Startschön " + // "#S_BarcelonaMailMergeMark2022-09-15T15:55:17Z8 Bookmark End#S_ParisMailMergeMark2022-09-15T15:55:17Z9 Bookmark Starterlebnisreich " + // "#S_ParisMailMergeMark2022-09-15T15:55:17Z9 Bookmark End." + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), sMerge.indexOf("Mein Urlaub war #S_BergeMailMergeMark")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(73), sMerge.indexOf("anstrengend ")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(201), sMerge.indexOf(u"schön ")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(323), sMerge.indexOf(u"erlebnisreich ")); } DECLARE_SHELL_MAILMERGE_TEST_SELECTION(testTdf95292, "linked-labels.odt", "10-testing-addresses.ods", "testing-addresses", 5) diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx index 8b698be6855d..b6d4af1fe739 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx @@ -1100,6 +1100,9 @@ CPPUNIT_TEST_FIXTURE(Test, testNumberingLevels) assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:pPr/w:numPr/w:ilvl [@w:val = '1']", 1); xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Note: _Toc and _Ref hidden bookmarks are imported from OOXML as normal bookmarks. + // Without hiding them from visible bookmarks (SwBookmarkPortion), this line would be + // shown as "A.2.1 [[[[[.DESCRIPTION]]]] with XML layout dump "A.2.1 #_Ref... Bookmark Start..." assertXPath(pXmlDoc, "//body/txt[5]/LineBreak", "Line", "A.2.1 .DESCRIPTION"); } diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx index 9b6bb5e7c08c..d10b5287756a 100644 --- a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx +++ b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx @@ -37,6 +37,7 @@ #include <com/sun/star/text/WrapTextMode.hpp> #include <com/sun/star/text/XDependentTextField.hpp> #include <com/sun/star/text/XFormField.hpp> +#include <com/sun/star/text/XParagraphCursor.hpp> #include <com/sun/star/text/XTextFieldsSupplier.hpp> #include <com/sun/star/text/XTextFrame.hpp> #include <com/sun/star/text/XTextFramesSupplier.hpp> @@ -388,12 +389,39 @@ CPPUNIT_TEST_FIXTURE(Test, testN758883) xmlDocUniquePtr pXmlDoc = parseLayoutDump(); assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout/SwFieldPortion[1]", "font-height", "220"); + // hidden _Toc and _Ref bookmarks are not visible in Visible bookmarks mode + // This was PortionType::Bookmark + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout/SwLinePortion[1]", "type", "PortionType::Text"); + + // insert a not hidden bookmark + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextRange> xTextRange = xTextDocument->getText(); + uno::Reference<text::XText> xText = xTextRange->getText(); + uno::Reference<text::XParagraphCursor> xCursor(xText->createTextCursor(), uno::UNO_QUERY); + uno::Reference<lang::XMultiServiceFactory> xFact(mxComponent, uno::UNO_QUERY); + // creating bookmark "BookmarkTest" + uno::Reference<text::XTextContent> xBookmark( + xFact->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY); + uno::Reference<container::XNamed> xBookmarkName(xBookmark, uno::UNO_QUERY); + xBookmarkName->setName("BookmarkTest"); + // moving cursor to the end of paragraph + xCursor->gotoEndOfParagraph(true); + // inserting the bookmark in paragraph + xText->insertTextContent(xCursor, xBookmark, true); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // check the bookmark portions are of the expected height assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout/SwLinePortion[1]", "type", "PortionType::Bookmark"); assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout/SwLinePortion[1]", "height", "253"); assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout/SwLinePortion[3]", "type", "PortionType::Bookmark"); assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout/SwLinePortion[3]", "height", "253"); + // tdf#150947 check a11y of the newly inserted bookmark portions + assertXPath(pXmlDoc, "/root/page/body/txt/Special[2]", "rText", "#BookmarkTest Bookmark Start"); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[3]", "rText", "#BookmarkTest Bookmark End"); + /* * Next problem was that the page margin contained the width of the page border as well. * diff --git a/sw/source/core/access/accportions.cxx b/sw/source/core/access/accportions.cxx index 9f43435a7dc4..084498e98100 100644 --- a/sw/source/core/access/accportions.cxx +++ b/sw/source/core/access/accportions.cxx @@ -163,7 +163,8 @@ void SwAccessiblePortionData::Special( sDisplay = rText + OUStringChar(m_pTextFrame->GetText()[sal_Int32(m_nViewPosition)]); break; case PortionType::Bookmark: - // TODO + if ( m_pViewOptions->IsShowBookmarks() ) + sDisplay = rText + " "; break; default: sDisplay = rText; diff --git a/sw/source/core/inc/scriptinfo.hxx b/sw/source/core/inc/scriptinfo.hxx index 77ed50e497d5..28d0ea3f2014 100644 --- a/sw/source/core/inc/scriptinfo.hxx +++ b/sw/source/core/inc/scriptinfo.hxx @@ -72,7 +72,7 @@ private: std::deque<TextFrameIndex> m_NoKashidaLine; std::deque<TextFrameIndex> m_NoKashidaLineEnd; std::vector<TextFrameIndex> m_HiddenChg; - std::vector<std::tuple<TextFrameIndex, MarkKind, Color>> m_Bookmarks; + std::vector<std::tuple<TextFrameIndex, MarkKind, Color, OUString>> m_Bookmarks; //! Records a single change in compression. struct CompressionChangeInfo { @@ -185,8 +185,8 @@ public: } TextFrameIndex NextHiddenChg(TextFrameIndex nPos) const; TextFrameIndex NextBookmark(TextFrameIndex nPos) const; - MarkKind GetBookmark(TextFrameIndex nPos) const; - std::optional<std::vector<Color>> GetBookmarkColors(TextFrameIndex nPos) const; + std::vector<std::tuple<MarkKind, Color, OUString>> + GetBookmarks(TextFrameIndex const nPos); static void CalcHiddenRanges(const SwTextNode& rNode, MultiSelection& rHiddenMulti, std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> * pBookmarks); diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 74057e743cf6..320133b0ffce 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -1417,36 +1417,13 @@ SwLinePortion *SwTextFormatter::WhichFirstPortion(SwTextFormatInfo &rInf) // check this *last* so that BuildMultiPortion() can find it! if (!pPor && rInf.CheckCurrentPosBookmark()) { - auto const bookmark(m_pScriptInfo->GetBookmark(rInf.GetIdx())); - if (static_cast<bool>(bookmark)) + const auto& bookmark = m_pScriptInfo->GetBookmarks(rInf.GetIdx()); + if (!bookmark.empty()) { - sal_Unicode mark; - if ((bookmark & (SwScriptInfo::MarkKind::Start|SwScriptInfo::MarkKind::End)) - == (SwScriptInfo::MarkKind::Start|SwScriptInfo::MarkKind::End)) - { - //mark = u'\u2336'; // not in OpenSymbol :( - mark = '|'; - // hmm ... paint U+2345 over U+2346 should be same width? - // and U+237F // or U+2E20/U+2E21 - } - else if (bookmark & SwScriptInfo::MarkKind::Start) - { - mark = '['; - } - else if (bookmark & SwScriptInfo::MarkKind::End) - { - mark = ']'; - } - else - { - assert(bookmark & SwScriptInfo::MarkKind::Point); - mark = '|'; - } - - // collect custom bookmark colors - auto rColors = m_pScriptInfo->GetBookmarkColors(rInf.GetIdx()); + // only for character width, maybe replaced with ] later + sal_Unicode mark = '['; - pPor = new SwBookmarkPortion(mark, rColors); + pPor = new SwBookmarkPortion(mark, bookmark); } } diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx index 29df569143e7..d11d67414b62 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -948,7 +948,7 @@ static void InitBookmarks( std::vector<sw::Extent>::const_iterator const end, TextFrameIndex nOffset, std::vector<std::pair<sw::mark::IBookmark const*, SwScriptInfo::MarkKind>> & rBookmarks, - std::vector<std::tuple<TextFrameIndex, SwScriptInfo::MarkKind, Color>> & o_rBookmarks) + std::vector<std::tuple<TextFrameIndex, SwScriptInfo::MarkKind, Color, OUString>> & o_rBookmarks) { SwTextNode const*const pNode(iter->pNode); for (auto const& it : rBookmarks) @@ -987,7 +987,7 @@ static void InitBookmarks( } else { - o_rBookmarks.emplace_back(nOffset, it.second, c); + o_rBookmarks.emplace_back(nOffset, it.second, c, it.first->GetName()); break; } } @@ -1006,7 +1006,7 @@ static void InitBookmarks( { o_rBookmarks.emplace_back( nOffset + TextFrameIndex(rStart.GetContentIndex() - iter->nStart), - it.second, c); + it.second, c, it.first->GetName()); break; } } @@ -1025,7 +1025,7 @@ static void InitBookmarks( } else { - o_rBookmarks.emplace_back(nOffset, it.second, c); + o_rBookmarks.emplace_back(nOffset, it.second, c, it.first->GetName()); } } break; @@ -1054,7 +1054,7 @@ static void InitBookmarks( } else { - o_rBookmarks.emplace_back(nOffset, it.second, c); + o_rBookmarks.emplace_back(nOffset, it.second, c, it.first->GetName()); break; } } @@ -1062,7 +1062,7 @@ static void InitBookmarks( { o_rBookmarks.emplace_back( nOffset + TextFrameIndex(rEnd.GetContentIndex() - iter->nStart), - it.second, c); + it.second, c, it.first->GetName()); break; } else @@ -1096,7 +1096,7 @@ static void InitBookmarks( { o_rBookmarks.emplace_back( nOffset + TextFrameIndex(rPos.GetContentIndex() - iter->nStart), - it.second, c); + it.second, c, it.first->GetName()); } break; } @@ -1239,13 +1239,13 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode, switch (it.second) { case MarkKind::Start: - m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkStart().GetContentIndex()), it.second, c); + m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkStart().GetContentIndex()), it.second, c, it.first->GetName()); break; case MarkKind::End: - m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkEnd().GetContentIndex()), it.second, c); + m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkEnd().GetContentIndex()), it.second, c, it.first->GetName()); break; case MarkKind::Point: - m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkPos().GetContentIndex()), it.second, c); + m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkPos().GetContentIndex()), it.second, c, it.first->GetName()); break; } } @@ -1942,39 +1942,39 @@ TextFrameIndex SwScriptInfo::NextBookmark(TextFrameIndex const nPos) const return TextFrameIndex(COMPLETE_STRING); } -auto SwScriptInfo::GetBookmark(TextFrameIndex const nPos) const -> MarkKind +std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>> + SwScriptInfo::GetBookmarks(TextFrameIndex const nPos) { - MarkKind ret{0}; + std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>> aColors; for (auto const& it : m_Bookmarks) { if (nPos == std::get<0>(it)) { - ret |= std::get<1>(it); + const OUString& sName = std::get<3>(it); + // filter hidden bookmarks imported from OOXML + // TODO import them as hidden bookmarks + if ( !( sName.startsWith("_Toc") || sName.startsWith("_Ref") ) ) + aColors.push_back(std::tuple<MarkKind, Color, + OUString>(std::get<1>(it), std::get<2>(it), std::get<3>(it))); } else if (nPos < std::get<0>(it)) { break; } } - return ret; -} -std::optional<std::vector<Color>> SwScriptInfo::GetBookmarkColors(TextFrameIndex const nPos) const -{ - std::optional<std::vector<Color>> ret; - std::vector<Color> aColors; - for (auto const& it : m_Bookmarks) - { - if (nPos == std::get<0>(it) && COL_TRANSPARENT != std::get<2>(it)) - { - aColors.push_back(std::get<2>(it)); - } - else if (nPos < std::get<0>(it)) - { - break; - } - } - return aColors.empty() ? ret : ret.emplace(aColors); + // sort bookmark boundary marks at the same position + // mark order: ] | [ + // color order: [c1 [c2 [c3 ... c3] c2] c1] + sort(aColors.begin(), aColors.end(), + [](std::tuple<MarkKind, Color, OUString>(a), std::tuple<MarkKind, Color, OUString>(b)) { + return (MarkKind::End == std::get<0>(a) && MarkKind::End != std::get<0>(b)) || + (MarkKind::Point == std::get<0>(a) && MarkKind::Start == std::get<0>(b)) || + // if both are end or start, order by color + (MarkKind::End == std::get<0>(a) && MarkKind::End == std::get<0>(b) && std::get<1>(a) < std::get<1>(b)) || + (MarkKind::Start == std::get<0>(a) && MarkKind::Start == std::get<0>(b) && std::get<1>(b) < std::get<1>(a));}); + + return aColors; } // Takes a string and replaced the hidden ranges with cChar. diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx index d5acb572d911..a5a02a42e057 100644 --- a/sw/source/core/text/porrst.cxx +++ b/sw/source/core/text/porrst.cxx @@ -49,6 +49,8 @@ #include <IDocumentDeviceAccess.hxx> #include <crsrsh.hxx> +#include <swtypes.hxx> +#include <strings.hrc> SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion, const FontLineStyle eUL, @@ -651,7 +653,7 @@ bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo, OUString & rOutString, SwFont & rFont, int & rDeltaY) const { // custom color is visible without field shading, too - if (!rTextPaintInfo.GetOpt().IsShowBookmarks(m_oColors.has_value())) + if (!rTextPaintInfo.GetOpt().IsShowBookmarks()) { return false; } @@ -783,30 +785,77 @@ void SwBookmarkPortion::Paint( const SwTextPaintInfo &rInf ) const assert(false); break; } + + // draw end marks before the character position + if ( m_nStart == 0 || m_nEnd == 0 ) + { + // single type boundary marks are there outside of the bookmark text + // some |text| here + // [[ ]] + if (m_nStart > 1) + aNewPos.AdjustX(mnHalfCharWidth * -2 * (m_oColors.size() - 1)); + } + else if ( m_nStart != 0 && m_nEnd != 0 ) + // both end and start boundary marks: adjust them around the bookmark position + // |te|xt| + // ]] [[ + aNewPos.AdjustX(mnHalfCharWidth * -(2 * m_nEnd - 1 + m_nPoint) ); + const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos ); - if ( m_oColors.has_value() ) + for ( const auto& it : m_oColors ) { // set bold for custom colored bookmark symbol // and draw multiple symbols showing all custom colors - aTmpFont.SetWeight( WEIGHT_BOLD, aTmpFont.GetActual() ); - for ( const Color& rColor : *m_oColors ) + aTmpFont.SetWeight( COL_TRANSPARENT == std::get<1>(it) ? WEIGHT_THIN : WEIGHT_BOLD, aTmpFont.GetActual() ); + aTmpFont.SetColor( COL_TRANSPARENT == std::get<1>(it) ? SwViewOption::GetFieldShadingsColor() : std::get<1>(it) ); + aOutString = OUString(std::get<0>(it) == SwScriptInfo::MarkKind::Start ? '[' : ']'); + + // MarkKind::Point: drawn I-beam (e.g. U+2336) as overlapping ][ + if ( std::get<0>(it) == SwScriptInfo::MarkKind::Point ) { - aTmpFont.SetColor( rColor ); + aNewPos.AdjustX(-mnHalfCharWidth * 5/16); + const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos ); rInf.DrawText( aOutString, *this ); - // place the next symbol after the previous one - // TODO: fix orientation and start/end - aNewPos.AdjustX(mnHalfCharWidth * 2.5); + // when the overlapping vertical lines are 50 pixel width on the screen, + // this distance (half width * 5/8) still results precise overlapping + aNewPos.AdjustX(mnHalfCharWidth * 5/8); const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos ); + aOutString = OUString('['); } - } - else rInf.DrawText( aOutString, *this ); + // place the next symbol after the previous one + // TODO: fix orientation and start/end + aNewPos.AdjustX(mnHalfCharWidth * 2); + const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos ); + } const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos ); } +void SwBookmarkPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + OUStringBuffer aStr; + for ( const auto& it : m_oColors ) + { + aStr.append("#" + std::get<2>(it) + " " + SwResId(STR_BOOKMARK_DEF_NAME)); + switch (std::get<0>(it)) + { + case SwScriptInfo::MarkKind::Point: + break; + case SwScriptInfo::MarkKind::Start: + aStr.append(" " + SwResId(STR_CAPTION_BEGINNING)); + break; + case SwScriptInfo::MarkKind::End: + aStr.append(" " + SwResId(STR_CAPTION_END)); + break; + } + } + + rPH.Special( GetLen(), aStr.makeStringAndClear(), GetWhichPor(), Height(), Width() ); +} + bool SwControlCharPortion::Format( SwTextFormatInfo &rInf ) { const SwLinePortion* pRoot = rInf.GetRoot(); diff --git a/sw/source/core/text/porrst.hxx b/sw/source/core/text/porrst.hxx index b09b838d253e..541586e39bcb 100644 --- a/sw/source/core/text/porrst.hxx +++ b/sw/source/core/text/porrst.hxx @@ -24,6 +24,7 @@ #include <txttypes.hxx> #include <txtfrm.hxx> #include <svx/ctredlin.hxx> +#include <scriptinfo.hxx> #include "porlin.hxx" #include "portxt.hxx" @@ -184,20 +185,36 @@ public: class SwBookmarkPortion : public SwControlCharPortion { // custom colors defined by metadata - std::optional<std::vector<Color>> m_oColors; + std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>> m_oColors; + // number of MarkKind marks + sal_Int16 m_nStart, m_nEnd, m_nPoint; + bool m_bHasCustomColor; public: - explicit SwBookmarkPortion(sal_Unicode const cChar, std::optional<std::vector<Color>>rColors) - : SwControlCharPortion(cChar), m_oColors(rColors) + explicit SwBookmarkPortion(sal_Unicode const cChar, std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>>rColors) + : SwControlCharPortion(cChar), m_oColors(rColors), m_nStart(0), m_nEnd(0), m_nPoint(0), m_bHasCustomColor(false) { SetWhichPor(PortionType::Bookmark); SetLen(TextFrameIndex(0)); + for (const auto& it : m_oColors) + { + if (std::get<0>(it) == SwScriptInfo::MarkKind::Start) + m_nStart++; + else if (std::get<0>(it) == SwScriptInfo::MarkKind::End) + m_nEnd++; + else + m_nPoint++; + + if (!m_bHasCustomColor && COL_TRANSPARENT != std::get<1>(it)) + m_bHasCustomColor = true; + } } virtual bool DoPaint(SwTextPaintInfo const& rInf, OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const override; virtual void Paint( const SwTextPaintInfo &rInf ) const override; virtual SwLinePortion * Compress() override { return this; } + virtual void HandlePortion(SwPortionHandler& rPH) const override; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/uiconfig/swriter/ui/optformataidspage.ui b/sw/uiconfig/swriter/ui/optformataidspage.ui index 71d57597ab39..c2595de4a362 100644 --- a/sw/uiconfig/swriter/ui/optformataidspage.ui +++ b/sw/uiconfig/swriter/ui/optformataidspage.ui @@ -235,9 +235,9 @@ <object class="GtkLabel" id="bookmarks_label"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="tooltip-text" translatable="yes" context="optformataidspage|bookmarks_label|tooltip_text">| indicates a point bookmark -[ ] indicate the start and end of a bookmark on a text range</property> - <property name="label">| [ ]</property> + <property name="tooltip-text" translatable="yes" context="optformataidspage|bookmarks_label|tooltip_text">⌶ indicates a point bookmark, +[ and ] indicate the start and end of a bookmark on a text range</property> + <property name="label">⌶ […]</property> </object> <packing> <property name="left-attach">1</property>