diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2021-02-18 12:57:21 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2021-02-18 15:07:28 +0100 |
commit | 3591c657ddee42a0a4b05fe87b13a737c5bfe868 (patch) | |
tree | dbcf6f6528f42a5accd131fc3ed25ad8bb445f3e /sw | |
parent | 787158d528838bdb25d86139ab02ecdd4e341bdb (diff) |
sw bibliography: make URLs in the bibliography table clickable
- Don't reuse the <text:index-entry-link-start> /
<text:index-entry-link-end> mechianism from
<text:table-of-content-entry-template>, because
<text:bibliography-entry-template> doesn't allow this
- Use STR_POOLCHR_INET_NORMAL as a char style, so the URL looks
clickable
- Allow absolute URLs for ToxAuthorityField::AUTH_FIELD_URL (other URLs
like ToC are always relative)
- Track the new text added by FillText() between the StartNewLink() and
CloseLink() to figure out what is the URL to be launched on click
Change-Id: I126fa06aecfff783e62b65a548228ff781b62c5c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111117
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
Diffstat (limited to 'sw')
-rw-r--r-- | sw/CppunitTest_sw_core_tox.mk | 76 | ||||
-rw-r--r-- | sw/Module_sw.mk | 1 | ||||
-rw-r--r-- | sw/inc/ToxLinkProcessor.hxx | 2 | ||||
-rw-r--r-- | sw/qa/core/test_ToxLinkProcessor.cxx | 14 | ||||
-rw-r--r-- | sw/qa/core/tox/tox.cxx | 71 | ||||
-rw-r--r-- | sw/source/core/tox/ToxLinkProcessor.cxx | 23 | ||||
-rw-r--r-- | sw/source/core/tox/ToxTextGenerator.cxx | 18 |
7 files changed, 186 insertions, 19 deletions
diff --git a/sw/CppunitTest_sw_core_tox.mk b/sw/CppunitTest_sw_core_tox.mk new file mode 100644 index 000000000000..4a99347239f5 --- /dev/null +++ b/sw/CppunitTest_sw_core_tox.mk @@ -0,0 +1,76 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# 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/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,sw_core_tox)) + +$(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_core_tox)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sw_core_tox, \ + sw/qa/core/tox/tox \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sw_core_tox, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + sfx \ + sw \ + swqahelper \ + test \ + unotest \ + utl \ + vcl \ + svt \ + tl \ + svl \ + svxcore \ +)) + +$(eval $(call gb_CppunitTest_use_externals,sw_core_tox,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_set_include,sw_core_tox,\ + -I$(SRCDIR)/sw/inc \ + -I$(SRCDIR)/sw/source/core/inc \ + -I$(SRCDIR)/sw/source/uibase/inc \ + -I$(SRCDIR)/sw/qa/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_api,sw_core_tox,\ + udkapi \ + offapi \ + oovbaapi \ +)) + +$(eval $(call gb_CppunitTest_use_ure,sw_core_tox)) +$(eval $(call gb_CppunitTest_use_vcl,sw_core_tox)) + +$(eval $(call gb_CppunitTest_use_rdb,sw_core_tox,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,sw_core_tox,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,sw_core_tox)) + +$(eval $(call gb_CppunitTest_use_uiconfigs,sw_core_tox, \ + modules/swriter \ + svt \ + svx \ +)) + +$(eval $(call gb_CppunitTest_use_more_fonts,sw_core_tox)) + +# vim: set noet sw=4 ts=4: diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk index fc4498ca29be..75bf13e75231 100644 --- a/sw/Module_sw.mk +++ b/sw/Module_sw.mk @@ -121,6 +121,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\ CppunitTest_sw_core_accessibilitycheck \ CppunitTest_sw_core_layout \ CppunitTest_sw_core_fields \ + CppunitTest_sw_core_tox \ CppunitTest_sw_core_frmedt \ CppunitTest_sw_core_txtnode \ CppunitTest_sw_core_objectpositioning \ diff --git a/sw/inc/ToxLinkProcessor.hxx b/sw/inc/ToxLinkProcessor.hxx index e53c537b758e..77307cf6b784 100644 --- a/sw/inc/ToxLinkProcessor.hxx +++ b/sw/inc/ToxLinkProcessor.hxx @@ -41,7 +41,7 @@ public: * STR_POOLCHR_TOXJUMP. */ void - CloseLink(sal_Int32 endPosition, const OUString& url); + CloseLink(sal_Int32 endPosition, const OUString& url, bool bRelative); /** Insert the found links as attributes to a text node */ void diff --git a/sw/qa/core/test_ToxLinkProcessor.cxx b/sw/qa/core/test_ToxLinkProcessor.cxx index cd18f453db87..a4bb1eac60f0 100644 --- a/sw/qa/core/test_ToxLinkProcessor.cxx +++ b/sw/qa/core/test_ToxLinkProcessor.cxx @@ -56,11 +56,11 @@ ToxLinkProcessorTest::NoExceptionIsThrownIfTooManyLinksAreClosed() { ToxLinkProcessor sut; sut.StartNewLink(0, STYLE_NAME_1); - sut.CloseLink(1, URL_1); + sut.CloseLink(1, URL_1, /*bRelative=*/true); // fdo#85872 actually it turns out the UI does something like this // so an exception must not be thrown! // should not succeed either (for backward compatibility) - sut.CloseLink(2, URL_1); + sut.CloseLink(2, URL_1, /*bRelative=*/true); CPPUNIT_ASSERT_EQUAL(1u, static_cast<unsigned>(sut.m_ClosedLinks.size())); CPPUNIT_ASSERT_EQUAL(1u, static_cast<unsigned>(sut.m_ClosedLinks.at(0)->mEndTextPos)); CPPUNIT_ASSERT_MESSAGE("no links are open", !sut.m_pStartedLink); @@ -72,10 +72,10 @@ ToxLinkProcessorTest::AddingAndClosingTwoOverlappingLinksResultsInOneClosedLink( ToxLinkProcessor sut; sut.StartNewLink(0, STYLE_NAME_1); sut.StartNewLink(0, STYLE_NAME_2); - sut.CloseLink(1, URL_1); + sut.CloseLink(1, URL_1, /*bRelative=*/true); // this should not cause an error, and should not succeed either // (for backward compatibility) - sut.CloseLink(1, URL_2); + sut.CloseLink(1, URL_2, /*bRelative=*/true); CPPUNIT_ASSERT_EQUAL(1u, static_cast<unsigned>(sut.m_ClosedLinks.size())); CPPUNIT_ASSERT_MESSAGE("no links are open", !sut.m_pStartedLink); // backward compatibility: the last start is closed by the first end @@ -108,7 +108,7 @@ ToxLinkProcessorTest::LinkIsCreatedCorrectly() ToxLinkProcessorWithOverriddenObtainPoolId sut; sut.StartNewLink(0, STYLE_NAME_1); - sut.CloseLink(1, URL_1); + sut.CloseLink(1, URL_1, /*bRelative=*/true); CPPUNIT_ASSERT_EQUAL_MESSAGE("Style is stored correctly in link", OUString(STYLE_NAME_1), sut.m_ClosedLinks.at(0)->mINetFormat.GetVisitedFormat()); CPPUNIT_ASSERT_EQUAL_MESSAGE("Url is stored correctly in link", OUString(URL_1), sut.m_ClosedLinks.at(0)->mINetFormat.GetValue()); @@ -122,9 +122,9 @@ ToxLinkProcessorTest::LinkSequenceIsPreserved() ToxLinkProcessorWithOverriddenObtainPoolId sut; sut.StartNewLink(0, STYLE_NAME_2); - sut.CloseLink(1, URL_2); + sut.CloseLink(1, URL_2, /*bRelative=*/true); sut.StartNewLink(1, STYLE_NAME_1); - sut.CloseLink(2, URL_1); + sut.CloseLink(2, URL_1, /*bRelative=*/true); // check first closed element CPPUNIT_ASSERT_EQUAL_MESSAGE("Style is stored correctly in link", diff --git a/sw/qa/core/tox/tox.cxx b/sw/qa/core/tox/tox.cxx new file mode 100644 index 000000000000..b9a075119293 --- /dev/null +++ b/sw/qa/core/tox/tox.cxx @@ -0,0 +1,71 @@ +/* -*- 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 <com/sun/star/text/BibliographyDataType.hpp> +#include <com/sun/star/text/ControlCharacter.hpp> +#include <com/sun/star/text/XDocumentIndex.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +#include <comphelper/propertyvalue.hxx> + +namespace +{ +/// Covers sw/source/core/tox/ fixes. +class Test : public SwModelTestBase +{ +}; + +CPPUNIT_TEST_FIXTURE(Test, testAuthorityLinkClick) +{ + // Create a document with a bibliography reference (of type WWW) in it. + createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xField( + xFactory->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aFields = { + comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW), + comphelper::makePropertyValue("Identifier", OUString("ARJ00")), + comphelper::makePropertyValue("Author", OUString("Ar, J")), + comphelper::makePropertyValue("Title", OUString("mytitle")), + comphelper::makePropertyValue("Year", OUString("2020")), + comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf")), + }; + xField->setPropertyValue("Fields", uno::makeAny(aFields)); + 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<text::XTextContent> xContent(xField, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xContent, /*bAbsorb=*/false); + // Create a bibliography table. + uno::Reference<text::XTextContent> xTable( + xFactory->createInstance("com.sun.star.text.Bibliography"), uno::UNO_QUERY); + xCursor->gotoEnd(/*bExpand=*/false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::APPEND_PARAGRAPH, + /*bAbsorb=*/false); + xText->insertTextContent(xCursor, xTable, /*bAbsorb=*/false); + + // Update it. + uno::Reference<text::XDocumentIndex> xTableIndex(xTable, uno::UNO_QUERY); + xTableIndex->update(); + + // Paragraph index: Reference, table header, table row. + // Portion index: ID, etc; then the URL. + auto aActual = getProperty<OUString>(getRun(getParagraph(3), 2), "HyperLinkURL"); + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.container.NoSuchElementException + // i.e. the URL was not clickable and the table row was a single text portion. + CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com/test.pdf"), aActual); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/tox/ToxLinkProcessor.cxx b/sw/source/core/tox/ToxLinkProcessor.cxx index 825e37c16f65..731a7c8a88c4 100644 --- a/sw/source/core/tox/ToxLinkProcessor.cxx +++ b/sw/source/core/tox/ToxLinkProcessor.cxx @@ -25,8 +25,7 @@ ToxLinkProcessor::StartNewLink(sal_Int32 startPosition, const OUString& characte startPosition, characterStyle); } -void -ToxLinkProcessor::CloseLink(sal_Int32 endPosition, const OUString& url) +void ToxLinkProcessor::CloseLink(sal_Int32 endPosition, const OUString& url, bool bRelative) { if (m_pStartedLink == nullptr) { @@ -38,12 +37,20 @@ ToxLinkProcessor::CloseLink(sal_Int32 endPosition, const OUString& url) return; } - // url contains '|' which must be encoded; also in some cases contains - // arbitrary strings that need to be encoded - assert(url[0] == '#'); // all links are internal - OUString const uri("#" + rtl::Uri::encode(url.copy(1), - rtl_UriCharClassUricNoSlash, - rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8)); + OUString uri; + if (bRelative) + { + // url contains '|' which must be encoded; also in some cases contains + // arbitrary strings that need to be encoded + assert(url[0] == '#'); // all links are internal + uri = "#" + + rtl::Uri::encode(url.copy(1), rtl_UriCharClassUricNoSlash, + rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8); + } + else + { + uri = url; + } std::unique_ptr<ClosedLink> pClosedLink( new ClosedLink(uri, m_pStartedLink->mStartPosition, endPosition)); diff --git a/sw/source/core/tox/ToxTextGenerator.cxx b/sw/source/core/tox/ToxTextGenerator.cxx index d836ec60f238..95952aa06cd2 100644 --- a/sw/source/core/tox/ToxTextGenerator.cxx +++ b/sw/source/core/tox/ToxTextGenerator.cxx @@ -38,6 +38,7 @@ #include <ToxTabStopTokenHandler.hxx> #include <txatbase.hxx> #include <modeltoviewhelper.hxx> +#include <strings.hrc> #include <osl/diagnose.h> #include <rtl/ustrbuf.hxx> @@ -192,6 +193,7 @@ ToxTextGenerator::GenerateText(SwDoc* pDoc, for(const auto& aToken : aPattern) // #i21237# { sal_Int32 nStartCharStyle = rText.getLength(); + OUString aCharStyleName = aToken.sCharStyleName; switch( aToken.eTokenType ) { case TOKEN_ENTRY_NO: @@ -250,7 +252,7 @@ ToxTextGenerator::GenerateText(SwDoc* pDoc, ++iter->second; url = "#" + OUString::number(iter->second) + url; } - mLinkProcessor->CloseLink(rText.getLength(), url); + mLinkProcessor->CloseLink(rText.getLength(), url, /*bRelative=*/true); } break; @@ -258,19 +260,29 @@ ToxTextGenerator::GenerateText(SwDoc* pDoc, { ToxAuthorityField eField = static_cast<ToxAuthorityField>(aToken.nAuthorityField); SwIndex aIdx( pTOXNd, rText.getLength() ); + if (eField == ToxAuthorityField::AUTH_FIELD_URL) + { + aCharStyleName = SwResId(STR_POOLCHR_INET_NORMAL); + mLinkProcessor->StartNewLink(rText.getLength(), aCharStyleName); + } rBase.FillText( *pTOXNd, aIdx, static_cast<sal_uInt16>(eField), pLayout ); + if (eField == ToxAuthorityField::AUTH_FIELD_URL) + { + OUString aURL(rText.subView(nStartCharStyle)); + mLinkProcessor->CloseLink(rText.getLength(), aURL, /*bRelative=*/false); + } } break; case TOKEN_END: break; } - if ( !aToken.sCharStyleName.isEmpty() ) + if (!aCharStyleName.isEmpty()) { SwCharFormat* pCharFormat; if( USHRT_MAX != aToken.nPoolId ) pCharFormat = pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( aToken.nPoolId ); else - pCharFormat = pDoc->FindCharFormatByName( aToken.sCharStyleName); + pCharFormat = pDoc->FindCharFormatByName(aCharStyleName); if (pCharFormat) { |