diff options
-rw-r--r-- | include/xmloff/xmltoken.hxx | 2 | ||||
-rw-r--r-- | sw/inc/authfld.hxx | 5 | ||||
-rw-r--r-- | sw/inc/helpids.h | 2 | ||||
-rw-r--r-- | sw/inc/strings.hrc | 2 | ||||
-rw-r--r-- | sw/inc/toxe.hxx | 2 | ||||
-rw-r--r-- | sw/qa/core/text/text.cxx | 154 | ||||
-rw-r--r-- | sw/source/core/fields/authfld.cxx | 30 | ||||
-rw-r--r-- | sw/source/core/fields/fldbas.cxx | 10 | ||||
-rw-r--r-- | sw/source/core/text/EnhancedPDFExportHelper.cxx | 149 | ||||
-rw-r--r-- | sw/source/ui/index/cnttab.cxx | 2 | ||||
-rw-r--r-- | sw/source/ui/index/swuiidxmrk.cxx | 34 | ||||
-rw-r--r-- | sw/source/uibase/docvw/edtwin2.cxx | 10 | ||||
-rw-r--r-- | sw/source/uibase/shells/textsh1.cxx | 14 | ||||
-rw-r--r-- | sw/source/uibase/utlui/initui.cxx | 2 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtsh2.cxx | 74 | ||||
-rw-r--r-- | sw/uiconfig/swriter/ui/bibliofragment.ui | 24 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 7 | ||||
-rw-r--r-- | xmloff/source/core/xmltoken.cxx | 2 | ||||
-rw-r--r-- | xmloff/source/text/txtflde.cxx | 10 | ||||
-rw-r--r-- | xmloff/source/text/txtfldi.cxx | 8 | ||||
-rw-r--r-- | xmloff/source/token/tokens.txt | 2 |
21 files changed, 495 insertions, 50 deletions
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 7c6cea0482d3..5262f3b3ab5e 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -3465,6 +3465,8 @@ namespace xmloff::token { XML_MARGIN_GUTTER, XML_LOCAL_URL, + XML_TARGET_URL, + XML_USE_TARGET_URL, // Math ml XML_DIR, diff --git a/sw/inc/authfld.hxx b/sw/inc/authfld.hxx index def78f913e14..26e4843a6ad2 100644 --- a/sw/inc/authfld.hxx +++ b/sw/inc/authfld.hxx @@ -199,8 +199,13 @@ public: OUString GetAuthority(const SwRootFrame *pLayout, const SwForm *pTOX = nullptr) const; + bool UseTargetURL() const; + bool HasURL() const; OUString GetAbsoluteURL() const; + + bool HasTargetURL() const; + OUString GetAbsoluteTargetURL() const; /** * Returns full URI for the URL, relative if specified * \param bRelative whether the path should be relative (when dealing with local files) diff --git a/sw/inc/helpids.h b/sw/inc/helpids.h index 65c49e22f311..78f52fd436ea 100644 --- a/sw/inc/helpids.h +++ b/sw/inc/helpids.h @@ -76,6 +76,8 @@ inline constexpr OStringLiteral HID_EDIT_FORMULA = "SW_HID_EDIT_FORMULA"; #define HID_AUTH_FIELD_CUSTOM5 "SW_HID_AUTH_FIELD_CUSTOM5" #define HID_AUTH_FIELD_ISBN "SW_HID_AUTH_FIELD_ISBN" #define HID_AUTH_FIELD_LOCAL_URL "SW_HID_AUTH_FIELD_LOCAL_URL" +#define HID_AUTH_FIELD_TARGET_URL "SW_HID_AUTH_FIELD_TARGET_URL" +#define HID_AUTH_FIELD_USE_TARGET_URL "SW_HID_AUTH_FIELD_USE_TARGET_URL" inline constexpr OStringLiteral HID_BUSINESS_FMT_PAGE = "SW_HID_BUSINESS_FMT_PAGE"; inline constexpr OStringLiteral HID_BUSINESS_FMT_PAGE_CONT = "SW_HID_BUSINESS_FMT_PAGE_CONT"; diff --git a/sw/inc/strings.hrc b/sw/inc/strings.hrc index 96e58d395164..8af6eef01a4d 100644 --- a/sw/inc/strings.hrc +++ b/sw/inc/strings.hrc @@ -816,6 +816,8 @@ #define STR_AUTH_FIELD_CUSTOM5 NC_("STR_AUTH_FIELD_CUSTOM5", "User-defined5") #define STR_AUTH_FIELD_ISBN NC_("STR_AUTH_FIELD_ISBN", "ISBN") #define STR_AUTH_FIELD_LOCAL_URL NC_("STR_AUTH_FIELD_LOCAL_URL", "Local copy") +#define STR_AUTH_FIELD_TARGET_URL NC_("STR_AUTH_FIELD_TARGET_URL", "Target URL") +#define STR_AUTH_FIELD_USE_TARGET_URL NC_("STR_AUTH_FIELD_USE_TARGET_URL", "Use Target URL as hyperlink") #define STR_IDXMRK_EDIT NC_("STR_IDXMRK_EDIT", "Edit Index Entry") #define STR_IDXMRK_INSERT NC_("STR_IDXMRK_INSERT", "Insert Index Entry") diff --git a/sw/inc/toxe.hxx b/sw/inc/toxe.hxx index b2f08e66c320..05084c84c2e8 100644 --- a/sw/inc/toxe.hxx +++ b/sw/inc/toxe.hxx @@ -115,6 +115,8 @@ enum ToxAuthorityField AUTH_FIELD_CUSTOM5, AUTH_FIELD_ISBN, AUTH_FIELD_LOCAL_URL, + AUTH_FIELD_TARGET_URL, + AUTH_FIELD_USE_TARGET_URL, AUTH_FIELD_END }; diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index d16e7b324656..7ace4df111c6 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -12,7 +12,9 @@ #include <memory> #include <com/sun/star/text/BibliographyDataType.hpp> +#include <com/sun/star/text/ControlCharacter.hpp> #include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/text/XDocumentIndex.hpp> #include <vcl/gdimtf.hxx> #include <vcl/filter/PDFiumLibrary.hxx> @@ -135,6 +137,158 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testBibliographyUrlPdfExport) CPPUNIT_ASSERT(pPdfPage->hasLinks()); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testBibliographyUrlPdfExport2) +{ + // Given a document with a bibliography entry field: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + { + return; + } + 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("AT")), + comphelper::makePropertyValue("Author", OUString("Author")), + comphelper::makePropertyValue("Title", OUString("Title")), + comphelper::makePropertyValue("URL", OUString("#page=1")), + }; + xField->setPropertyValue("Fields", uno::Any(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); + + // When exporting to PDF: + save("writer_pdf_Export"); + + // Then make sure the field links when the Target URL is set + // (this test is important, isn't the same as the one above) + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(pPdfPage->hasLinks()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testBibliographyUrlPdfExport3) +{ + // Given a document with a bibliography entry field: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + { + return; + } + 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("AT")), + comphelper::makePropertyValue("Author", OUString("Author")), + comphelper::makePropertyValue("Title", OUString("Title")), + comphelper::makePropertyValue("TargetURL", OUString("#page=1")), + }; + xField->setPropertyValue("Fields", uno::Any(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); + + // When exporting to PDF: + save("writer_pdf_Export"); + + // Then make sure there are no links since UseTargetURL is not set + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(!pPdfPage->hasLinks()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testBibliographyUrlPdfExport4) +{ + // Given a document with a bibliography entry field: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + { + return; + } + 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("AT")), + comphelper::makePropertyValue("Author", OUString("Author")), + comphelper::makePropertyValue("Title", OUString("Title")), + comphelper::makePropertyValue("TargetURL", OUString("#page=1")), + comphelper::makePropertyValue("UseTargetURL", OUString("true")), + }; + xField->setPropertyValue("Fields", uno::Any(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); + + // When exporting to PDF: + save("writer_pdf_Export"); + + // Then make sure the field links when the Target URL is set + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(pPdfPage->hasLinks()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testBibliographyUrlPdfExport5) +{ + // Given a document with a bibliography entry field: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + { + return; + } + 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("AT")), + comphelper::makePropertyValue("Author", OUString("Author")), + comphelper::makePropertyValue("Title", OUString("Title")), + comphelper::makePropertyValue("UseTargetURL", OUString("true")), + }; + xField->setPropertyValue("Fields", uno::Any(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 the table + uno::Reference<text::XDocumentIndex> xTableIndex(xTable, uno::UNO_QUERY); + xTableIndex->update(); + + // When exporting to PDF: + save("writer_pdf_Export"); + + // Then make sure the mark links to the table when table is present + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(pPdfPage->hasLinks()); +} + CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTabOverMarginSection) { createSwDoc("tabovermargin-section.fodt"); diff --git a/sw/source/core/fields/authfld.cxx b/sw/source/core/fields/authfld.cxx index 44d1e39dd3ae..d1d50506d741 100644 --- a/sw/source/core/fields/authfld.cxx +++ b/sw/source/core/fields/authfld.cxx @@ -634,6 +634,12 @@ OUString SwAuthorityField::GetAuthority(const SwRootFrame* pLayout, const SwForm return aText; } +bool SwAuthorityField::UseTargetURL() const +{ + const OUString& rValue = GetAuthEntry()->GetAuthorField(AUTH_FIELD_USE_TARGET_URL); + return rValue.toAsciiLowerCase() == "true"; +} + bool SwAuthorityField::HasURL() const { const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL); @@ -650,6 +656,22 @@ OUString SwAuthorityField::GetAbsoluteURL() const INetURLObject::DecodeMechanism::WithCharset); } +bool SwAuthorityField::HasTargetURL() const +{ + const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_TARGET_URL); + return !rURL.isEmpty(); +} + +OUString SwAuthorityField::GetAbsoluteTargetURL() const +{ + const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_TARGET_URL); + SwDoc* pDoc = static_cast<SwAuthorityFieldType*>(GetTyp())->GetDoc(); + SwDocShell* pDocShell = pDoc->GetDocShell(); + OUString aBasePath = pDocShell->getDocumentBaseURL(); + return INetURLObject::GetAbsURL(aBasePath, rURL, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::WithCharset); +} + OUString SwAuthorityField::GetURI(bool bRelative) const { OUString sTmp = GetFieldText(AUTH_FIELD_URL); @@ -751,7 +773,9 @@ const char* const aFieldNames[] = "Custom4", "Custom5", "ISBN", - "LocalURL" + "LocalURL", + "TargetURL", + "UseTargetURL" }; void SwAuthEntry::dumpAsXml(xmlTextWriterPtr pWriter) const @@ -808,8 +832,8 @@ bool SwAuthorityField::PutValue( const Any& rAny, sal_uInt16 /*nWhichId*/ ) if(!(rAny >>= aParam)) return false; - OUStringBuffer sBuf(+AUTH_FIELD_LOCAL_URL); - comphelper::string::padToLength(sBuf, AUTH_FIELD_LOCAL_URL, TOX_STYLE_DELIMITER); + OUStringBuffer sBuf(+(AUTH_FIELD_END - 1)); + comphelper::string::padToLength(sBuf, (AUTH_FIELD_END - 1), TOX_STYLE_DELIMITER); OUString sToSet(sBuf.makeStringAndClear()); for(const PropertyValue& rParam : std::as_const(aParam)) { diff --git a/sw/source/core/fields/fldbas.cxx b/sw/source/core/fields/fldbas.cxx index 71afe0d7e425..80ae487f93fe 100644 --- a/sw/source/core/fields/fldbas.cxx +++ b/sw/source/core/fields/fldbas.cxx @@ -423,7 +423,8 @@ bool SwField::HasClickHdl() const case SwFieldIds::GetRef: case SwFieldIds::Macro: case SwFieldIds::Input: - case SwFieldIds::Dropdown : + case SwFieldIds::Dropdown: + case SwFieldIds::TableOfAuthorities: bRet = true; break; @@ -431,13 +432,6 @@ bool SwField::HasClickHdl() const bRet = static_cast<const SwSetExpField*>(this)->GetInputFlag(); break; - case SwFieldIds::TableOfAuthorities: - { - const auto pAuthorityField = static_cast<const SwAuthorityField*>(this); - bRet = pAuthorityField->HasURL(); - break; - } - default: break; } return bRet; diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx index 053b67990eda..1dde77106624 100644 --- a/sw/source/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -803,6 +803,13 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType ) bLanguage = true; break; + case vcl::PDFWriter::BibEntry : + bTextDecorationType = + bBaselineShift = + bLinkAttribute = + bLanguage = true; + break; + default: break; } @@ -2246,6 +2253,50 @@ void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks() return; } + // Create PDF destinations for bibliography table entries + std::vector<std::tuple<const SwTOXBase*, const OUString*, sal_Int32>> vDestinations; + // string is the row node text, sal_Int32 is number of the destination + // Note: This way of iterating doesn't seem to take into account TOXes + // that are in a frame, probably in some other cases too + { + mrSh.GotoPage(1); + while (mrSh.GotoNextTOXBase()) + { + const SwTOXBase* pIteratedTOX = nullptr; + while ((pIteratedTOX = mrSh.GetCurTOX()) != nullptr + && pIteratedTOX->GetType() == TOX_AUTHORITIES) + { + if (const SwNode& rCurrentNode = mrSh.GetCursor()->GetPoint()->GetNode(); + rCurrentNode.GetNodeType() == SwNodeType::Text) + { + if (mrSh.GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType() + == SectionType::ToxContent) // this checks it's not a heading + { + // Destination Rectangle + const SwRect& rDestRect = mrSh.GetCharRect(); + + const SwPageFrame* pCurrPage = + static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() ); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + // Destination Export + if ( -1 != nDestPageNum ) + { + tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect())); + const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum); + const OUString* vNodeText = &static_cast<const SwTextNode*>(&rCurrentNode)->GetText(); + vDestinations.emplace_back(pIteratedTOX, vNodeText, nDestId); + } + } + } + mrSh.MovePara(GoNextPara, fnParaStart); + } + } + } + + // Generate links to matching entries in the bibliography tables std::vector<SwFormatField*> aFields; SwFieldType* pType = mrSh.GetFieldType(SwFieldIds::TableOfAuthorities, OUString()); if (!pType) @@ -2264,38 +2315,90 @@ void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks() const auto& rAuthorityField = *static_cast<const SwAuthorityField*>(pFormatField->GetField()); - if (!rAuthorityField.HasURL()) - { - continue; - } - const OUString& rURL = rAuthorityField.GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL); - const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode(); - if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField)) + if ((!rAuthorityField.UseTargetURL() && rAuthorityField.HasURL()) + || (rAuthorityField.UseTargetURL() && rAuthorityField.HasTargetURL())) { - continue; - } + // Since the conditions indicate usage of a URL link to it + const OUString& rURL = rAuthorityField.GetAuthEntry()->GetAuthorField( + rAuthorityField.UseTargetURL() ? AUTH_FIELD_TARGET_URL : AUTH_FIELD_URL); - OUString const content(rAuthorityField.ExpandField(true, mrSh.GetLayout())); + const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode(); + if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField)) + { + continue; + } - // Select the field. - mrSh.SwCursorShell::SetMark(); - mrSh.SwCursorShell::Right(1, SwCursorSkipMode::Chars); + OUString const content(rAuthorityField.ExpandField(true, mrSh.GetLayout())); - // Create the links. - for (const auto& rLinkRect : *mrSh.SwCursorShell::GetCursor_()) - { - for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect)) + // Select the field. + mrSh.SwCursorShell::SetMark(); + mrSh.SwCursorShell::Right(1, SwCursorSkipMode::Chars); + + // Create the links. + for (const auto& rLinkRect : *mrSh.SwCursorShell::GetCursor_()) { - tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect())); - sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, content, rLinkPageNum); - IdMapEntry aLinkEntry(rLinkRect, nLinkId); - s_aLinkIdMap.push_back(aLinkEntry); - pPDFExtOutDevData->SetLinkURL(nLinkId, rURL); + for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect)) + { + tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect())); + sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, content, rLinkPageNum); + IdMapEntry aLinkEntry(rLinkRect, nLinkId); + s_aLinkIdMap.push_back(aLinkEntry); + pPDFExtOutDevData->SetLinkURL(nLinkId, rURL); + } } + mrSh.SwCursorShell::ClearMark(); } + else if (rAuthorityField.UseTargetURL()) + { + // Since the bibliography mark doesn't have target URL, try linking to a bibliography table + sal_Int32 nDestId = -1; + + std::unordered_map<const SwTOXBase*, OUString> vFormattedFieldStrings; + for (const auto& rDestinationTuple : vDestinations) + { + if (vFormattedFieldStrings.find(std::get<0>(rDestinationTuple)) + == vFormattedFieldStrings.end()) + vFormattedFieldStrings.emplace(std::get<0>(rDestinationTuple), + rAuthorityField.GetAuthority(mrSh.GetLayout(), + &std::get<0>(rDestinationTuple)->GetTOXForm())); + + if (vFormattedFieldStrings.at(std::get<0>(rDestinationTuple)) == *std::get<1>(rDestinationTuple)) + { + nDestId = std::get<2>(rDestinationTuple); + break; + } + } + + if (nDestId == -1) + continue; + + const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode(); + if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField)) + { + continue; + } + + OUString const content(rAuthorityField.ExpandField(true, mrSh.GetLayout())); - mrSh.SwCursorShell::ClearMark(); + // Select the field. + mrSh.SwCursorShell::SetMark(); + mrSh.SwCursorShell::Right(1, SwCursorSkipMode::Chars); + + // Create the links. + for (const auto& rLinkRect : *mrSh.SwCursorShell::GetCursor_()) + { + for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect)) + { + tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect())); + sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, content, rLinkPageNum); + IdMapEntry aLinkEntry(rLinkRect, nLinkId); + s_aLinkIdMap.push_back(aLinkEntry); + pPDFExtOutDevData->SetLinkDest(nLinkId, nDestId); + } + } + mrSh.SwCursorShell::ClearMark(); + } } } diff --git a/sw/source/ui/index/cnttab.cxx b/sw/source/ui/index/cnttab.cxx index 9955a85cd3d3..ce234a14f293 100644 --- a/sw/source/ui/index/cnttab.cxx +++ b/sw/source/ui/index/cnttab.cxx @@ -1899,6 +1899,8 @@ namespace STR_AUTH_FIELD_CUSTOM5, STR_AUTH_FIELD_ISBN, STR_AUTH_FIELD_LOCAL_URL, + STR_AUTH_FIELD_TARGET_URL, + STR_AUTH_FIELD_USE_TARGET_URL, }; } diff --git a/sw/source/ui/index/swuiidxmrk.cxx b/sw/source/ui/index/swuiidxmrk.cxx index 656eacedae02..4c9ea292bee7 100644 --- a/sw/source/ui/index/swuiidxmrk.cxx +++ b/sw/source/ui/index/swuiidxmrk.cxx @@ -1091,6 +1091,7 @@ class SwCreateAuthEntryDlg_Impl : public weld::GenericDialogController std::unique_ptr<weld::Button> m_xLocalBrowseButton; std::unique_ptr<weld::CheckButton> m_xLocalPageCB; std::unique_ptr<weld::SpinButton> m_xLocalPageSB; + std::unique_ptr<weld::CheckButton> m_xUseTargetURLCB; DECL_LINK(IdentifierHdl, weld::ComboBox&, void); DECL_LINK(ShortNameHdl, weld::Entry&, void); @@ -1147,6 +1148,8 @@ const TextInfo aTextInfoArr[] = {AUTH_FIELD_ANNOTE, HID_AUTH_FIELD_ANNOTE }, {AUTH_FIELD_NOTE, HID_AUTH_FIELD_NOTE }, {AUTH_FIELD_URL, HID_AUTH_FIELD_URL }, + {AUTH_FIELD_USE_TARGET_URL, HID_AUTH_FIELD_USE_TARGET_URL }, + {AUTH_FIELD_TARGET_URL, HID_AUTH_FIELD_TARGET_URL }, {AUTH_FIELD_LOCAL_URL, HID_AUTH_FIELD_LOCAL_URL }, {AUTH_FIELD_CUSTOM1, HID_AUTH_FIELD_CUSTOM1 }, {AUTH_FIELD_CUSTOM2, HID_AUTH_FIELD_CUSTOM2 }, @@ -1567,6 +1570,8 @@ namespace STR_AUTH_FIELD_CUSTOM5, STR_AUTH_FIELD_ISBN, STR_AUTH_FIELD_LOCAL_URL, + STR_AUTH_FIELD_TARGET_URL, + STR_AUTH_FIELD_USE_TARGET_URL, }; } @@ -1655,6 +1660,29 @@ SwCreateAuthEntryDlg_Impl::SwCreateAuthEntryDlg_Impl(weld::Window* pParent, m_xIdentifierBox->set_help_id(aCurInfo.pHelpId); m_aFixedTexts.back()->set_mnemonic_widget(m_xIdentifierBox.get()); } + else if (AUTH_FIELD_USE_TARGET_URL == aCurInfo.nToxField) + { + m_pBoxes[nIndex] = m_aBuilders.back()->weld_box("togglebox"); + + if (bLeft) + m_aOrigContainers.back()->move(m_pBoxes[nIndex].get(), m_xLeft.get()); + else + m_aOrigContainers.back()->move(m_pBoxes[nIndex].get(), m_xRight.get()); + + m_pBoxes[nIndex]->set_grid_left_attach(1); + m_pBoxes[nIndex]->set_grid_top_attach(bLeft ? nLeftRow : nRightRow); + m_pBoxes[nIndex]->set_hexpand(true); + + m_xUseTargetURLCB = m_aBuilders.back()->weld_check_button("usetargeturlcb"); + m_xUseTargetURLCB->set_active(m_bNewEntryMode + || pFields[aCurInfo.nToxField].toAsciiLowerCase() == "true"); + m_xUseTargetURLCB->set_grid_left_attach(1); + m_xUseTargetURLCB->set_grid_top_attach(bLeft ? nLeftRow : nRightRow); + m_xUseTargetURLCB->set_hexpand(true); + m_xUseTargetURLCB->show(); + m_xUseTargetURLCB->set_help_id(aCurInfo.pHelpId); + m_aFixedTexts.back()->set_mnemonic_widget(m_xUseTargetURLCB.get()); + } else { m_pBoxes[nIndex] = m_aBuilders.back()->weld_box("vbox"); @@ -1747,6 +1775,12 @@ OUString SwCreateAuthEntryDlg_Impl::GetEntryText(ToxAuthorityField eField) cons return m_xIdentifierBox->get_active_text(); } + if (AUTH_FIELD_USE_TARGET_URL == eField) + { + assert(m_xUseTargetURLCB && "No UseTargetURL"); + return (m_xUseTargetURLCB->get_active() ? OUString("true") : OUString("false")); + } + for(int nIndex = 0; nIndex < AUTH_FIELD_END; nIndex++) { const TextInfo aCurInfo = aTextInfoArr[nIndex]; diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx index 573b08dc4f47..01c668be4586 100644 --- a/sw/source/uibase/docvw/edtwin2.cxx +++ b/sw/source/uibase/docvw/edtwin2.cxx @@ -375,11 +375,17 @@ void SwEditWin::RequestHelp(const HelpEvent &rEvt) const auto pAuthorityField = static_cast<const SwAuthorityField*>(pField); sText = pAuthorityField->GetAuthority(rSh.GetLayout()); - if (pAuthorityField->HasURL()) + if (!pAuthorityField->UseTargetURL() && pAuthorityField->HasURL()) { - const OUString& rURL = pAuthorityField->GetURI(false); + const OUString& rURL = pAuthorityField->GetAbsoluteURL(); sText += "\n" + SfxHelp::GetURLHelpText(rURL); } + else if (pAuthorityField->UseTargetURL() && pAuthorityField->HasTargetURL()) + { + const OUString& rURL = pAuthorityField->GetAbsoluteTargetURL(); + sText += "\n" + SfxHelp::GetURLHelpText(rURL); + } + break; } diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx index 156572854c19..f1d9522a89f7 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -1821,13 +1821,20 @@ void SwTextShell::Execute(SfxRequest &rReq) if (pField && pField->GetTyp()->Which() == SwFieldIds::TableOfAuthorities) { const auto& rAuthorityField = *static_cast<const SwAuthorityField*>(pField); - if (rAuthorityField.HasURL()) + if (!rAuthorityField.UseTargetURL() && rAuthorityField.HasURL()) { // Bibliography entry with URL also provides a hyperlink. const OUString& rURL = rAuthorityField.GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL); ::LoadURL(rWrtSh, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); } + else if (rAuthorityField.UseTargetURL() && rAuthorityField.HasTargetURL()) + { + // Bibliography entry with URL also provides a hyperlink. + const OUString& rURL + = rAuthorityField.GetAuthEntry()->GetAuthorField(AUTH_FIELD_TARGET_URL); + ::LoadURL(rWrtSh, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); + } } } } @@ -2552,7 +2559,10 @@ void SwTextShell::GetState( SfxItemSet &rSet ) { // Bibliography entry with URL also provides a hyperlink. const auto& rAuthorityField = *static_cast<const SwAuthorityField*>(pField); - bAuthorityFieldURL = rAuthorityField.HasURL(); + if (!rAuthorityField.UseTargetURL()) + bAuthorityFieldURL = rAuthorityField.HasURL(); + else + bAuthorityFieldURL = rAuthorityField.HasTargetURL(); } if (SfxItemState::SET > aSet.GetItemState(RES_TXTATR_INETFMT, false) && !bAuthorityFieldURL) diff --git a/sw/source/uibase/utlui/initui.cxx b/sw/source/uibase/utlui/initui.cxx index 4438935585b9..51e2b8e8894e 100644 --- a/sw/source/uibase/utlui/initui.cxx +++ b/sw/source/uibase/utlui/initui.cxx @@ -236,6 +236,8 @@ namespace STR_AUTH_FIELD_CUSTOM5, STR_AUTH_FIELD_ISBN, STR_AUTH_FIELD_LOCAL_URL, + STR_AUTH_FIELD_TARGET_URL, + STR_AUTH_FIELD_USE_TARGET_URL, }; } diff --git a/sw/source/uibase/wrtsh/wrtsh2.cxx b/sw/source/uibase/wrtsh/wrtsh2.cxx index b9995c0922b4..3249b39e86eb 100644 --- a/sw/source/uibase/wrtsh/wrtsh2.cxx +++ b/sw/source/uibase/wrtsh/wrtsh2.cxx @@ -49,6 +49,7 @@ #include <swabstdlg.hxx> #include <SwRewriter.hxx> #include <authfld.hxx> +#include <ndtxt.hxx> #include <com/sun/star/document/XDocumentProperties.hpp> #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> @@ -342,8 +343,11 @@ void SwWrtShell::ClickToField(const SwField& rField, bool bExecHyperlinks) { addCurrentPosition(); - // cross reference field must not be selected because it moves the cursor - if (SwFieldIds::GetRef != rField.GetTyp()->Which()) + // Since the cross reference and bibliography mark move the cursor, + // only select the field if it's not a Ctrl+Click + if (!bExecHyperlinks + || (SwFieldIds::GetRef != rField.GetTyp()->Which() + && SwFieldIds::TableOfAuthorities != rField.GetTyp()->Which())) { StartAllAction(); Right( SwCursorSkipMode::Chars, true, 1, false ); // Select the field. @@ -405,22 +409,74 @@ void SwWrtShell::ClickToField(const SwField& rField, bool bExecHyperlinks) case SwFieldIds::TableOfAuthorities: { if (!bExecHyperlinks) + break; // Since it's not a Ctrl+Click, do not jump anywhere + + Point vStartPoint = GetCursor_()->GetPtPos(); + const SwAuthorityField* pField = static_cast<const SwAuthorityField*>(&rField); + + if (!pField->UseTargetURL() && pField->HasURL()) { - break; - } + // Since user didn't opt in to use Target URL, open standard URL - auto pField = static_cast<const SwAuthorityField*>(&rField); - if (!pField->HasURL()) + const OUString& rURL = pField->GetAbsoluteURL(); + ::LoadURL(*this, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); + } + else if (pField->UseTargetURL() && pField->HasTargetURL()) { - break; + // Since user opted in to use Target URL and field has Target URL, use it + + const OUString& rURL = pField->GetAbsoluteTargetURL(); + ::LoadURL(*this, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); } + else if (pField->UseTargetURL()) + { + // Since user opted in to use Target URL but the field doesn't have Target URL, + // try finding matching bibliography table line + + const bool bWasViewLocked = IsViewLocked(); + LockView(true); - const OUString& rURL = pField->GetAbsoluteURL(); - ::LoadURL(*this, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); + // Note: This way of iterating doesn't seem to take into account TOXes + // that are in a frame, probably in some other cases too + GotoPage(1); + while (GotoNextTOXBase()) + { + const SwTOXBase* pIteratedTOX = nullptr; + const SwTOXBase* pPreviousTOX = nullptr; + OUString vFieldText; + while ((pIteratedTOX = GetCurTOX()) != nullptr + && pIteratedTOX->GetType() == TOX_AUTHORITIES) + { + if (pIteratedTOX != pPreviousTOX) + vFieldText = pField->GetAuthority(GetLayout(), &pIteratedTOX->GetTOXForm()); + + if (const SwNode& rCurrentNode = GetCursor()->GetPoint()->GetNode(); + rCurrentNode.GetNodeType() == SwNodeType::Text + && (GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType() + == SectionType::ToxContent) // this checks it's not a heading + && static_cast<const SwTextNode*>(&rCurrentNode)->GetText() == vFieldText) + { + // Since a node has been found that is a text node, isn't a heading, + // and has text matching to text generated by the field, jump to it + LockView(bWasViewLocked); + ShowCursor(); + return; + } + pPreviousTOX = pIteratedTOX; + FwdPara(); + } + } + // Since a matching node has not been found, return to original position + SetCursor(&vStartPoint); + LockView(bWasViewLocked); + } } break; case SwFieldIds::GetRef: + if (!bExecHyperlinks) + break; + StartAllAction(); SwCursorShell::GotoRefMark( static_cast<const SwGetRefField&>(rField).GetSetRefName(), static_cast<const SwGetRefField&>(rField).GetSubType(), diff --git a/sw/uiconfig/swriter/ui/bibliofragment.ui b/sw/uiconfig/swriter/ui/bibliofragment.ui index f35db697e1e7..2652711b4472 100644 --- a/sw/uiconfig/swriter/ui/bibliofragment.ui +++ b/sw/uiconfig/swriter/ui/bibliofragment.ui @@ -109,6 +109,30 @@ </packing> </child> <child> + <object class="GtkBox" id="togglebox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkCheckButton" id="usetargeturlcb"> + <property name="label" translatable="yes" context=""></property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> <object class="GtkComboBoxText" id="listbox"> <property name="visible">True</property> <property name="can_focus">False</property> diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 8b1a8b6287da..2b17a18dd33b 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -3631,7 +3631,12 @@ we check in the following sequence: } else { - INetURLObject aNewURL(rtl::Uri::convertRelToAbs(m_aContext.BaseURL, url)); + INetURLObject aNewURL(rtl::Uri::convertRelToAbs( + (m_aContext.BaseURL.getLength() > 0 ? + m_aContext.BaseURL : + //use dummy location if empty + u"http://ahost.ax"), + url)); aTargetURL = aNewURL; //reassign the new target URL //recompute the target protocol, with the new URL diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 7eb6f14eee54..daa658011ae3 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3469,6 +3469,8 @@ namespace xmloff::token { TOKEN("margin-gutter", XML_MARGIN_GUTTER), TOKEN("local-url", XML_LOCAL_URL), + TOKEN("target-url", XML_TARGET_URL), + TOKEN("use-target-url", XML_USE_TARGET_URL), TOKEN("dir", XML_DIR ), TOKEN("displaystyle", XML_DISPLAYSTYLE ), diff --git a/xmloff/source/text/txtflde.cxx b/xmloff/source/text/txtflde.cxx index 58ad8604f0a1..71093e61420a 100644 --- a/xmloff/source/text/txtflde.cxx +++ b/xmloff/source/text/txtflde.cxx @@ -2708,7 +2708,7 @@ void XMLTextFieldExport::ProcessBibliographyData( if (!sStr.isEmpty()) { XMLTokenEnum eElement = MapBibliographyFieldName(rProp.Name); - if (eElement == XML_URL || eElement == XML_LOCAL_URL) + if (eElement == XML_URL || eElement == XML_LOCAL_URL || eElement == XML_TARGET_URL) { sStr = GetExport().GetRelativeReference(sStr); } @@ -3414,6 +3414,14 @@ enum XMLTokenEnum XMLTextFieldExport::MapBibliographyFieldName(std::u16string_vi { eName = XML_LOCAL_URL; } + else if (sName == u"TargetURL") + { + eName = XML_TARGET_URL; + } + else if (sName == u"UseTargetURL") + { + eName = XML_USE_TARGET_URL; + } else { SAL_WARN("xmloff.text", "Unknown bibliography info data"); diff --git a/xmloff/source/text/txtfldi.cxx b/xmloff/source/text/txtfldi.cxx index af2bd8aa2ac1..e2fad89cac9d 100644 --- a/xmloff/source/text/txtfldi.cxx +++ b/xmloff/source/text/txtfldi.cxx @@ -2969,7 +2969,7 @@ void XMLBibliographyFieldImportContext::startFastElement( else { OUString aStringValue = aIter.toString(); - if (nToken == XML_URL || nToken == XML_LOCAL_URL) + if (nToken == XML_URL || nToken == XML_LOCAL_URL || nToken == XML_TARGET_URL) { aStringValue = GetImport().GetAbsoluteReference(aStringValue); } @@ -3113,6 +3113,12 @@ const char* XMLBibliographyFieldImportContext::MapBibliographyFieldName( case XML_LOCAL_URL: pName = "LocalURL"; break; + case XML_TARGET_URL: + pName = "TargetURL"; + break; + case XML_USE_TARGET_URL: + pName = "UseTargetURL"; + break; default: assert(false && "Unknown bibliography info data"); pName = nullptr; diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 576efe9b8bd5..098d8dd4c501 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3216,6 +3216,8 @@ page-content-top page-content-bottom margin-gutter local-url +target-url +use-target-url dir displaystyle infinity |