diff options
author | Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de> | 2021-07-19 08:11:43 +0200 |
---|---|---|
committer | Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de> | 2021-07-26 14:13:53 +0200 |
commit | a23b44fd9f0119f7ea3523e32875f55c1a07c1cd (patch) | |
tree | 2a9ffc63b7186fea9d0cfea8660d95d30a8f6684 | |
parent | 3c3b9ad8886da916027f0fb940a2df822d63d4d7 (diff) |
tdf#123626 Allow adding hyperlinks to shapes
* Support hyperlinks on Shapes in Writer
* Add menu items
* Add context menu items
* ODF import/export + test
* OOXML import/export + test
Change-Id: I7269064c4cabd16fdb8259a48429c508184d3ccf
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119164
Tested-by: Jenkins
Reviewed-by: Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de>
31 files changed, 269 insertions, 43 deletions
diff --git a/include/editeng/unoprnms.hxx b/include/editeng/unoprnms.hxx index 011e504ae7f4..f99ddac50414 100644 --- a/include/editeng/unoprnms.hxx +++ b/include/editeng/unoprnms.hxx @@ -163,6 +163,8 @@ #define UNO_NAME_MISC_OBJ_TITLE "Title" #define UNO_NAME_MISC_OBJ_DESCRIPTION "Description" +#define UNO_NAME_HYPERLINK "Hyperlink" + #define UNO_NAME_GRAPHOBJ_FILLBITMAP "GraphicObjectFillBitmap" #define UNO_NAME_GRAPHOBJ_REPLACEMENT_GRAPHIC "ReplacementGraphic" #define UNO_NAME_GRAPHOBJ_GRAFSTREAMURL "GraphicStreamURL" diff --git a/include/oox/vml/vmlshape.hxx b/include/oox/vml/vmlshape.hxx index 0e50e5b6ee1f..d90ecc2865bf 100644 --- a/include/oox/vml/vmlshape.hxx +++ b/include/oox/vml/vmlshape.hxx @@ -112,6 +112,7 @@ struct ShapeTypeModel OptValue<OUString> moCropRight; ///< Specifies how much to crop the image from the right in as a fraction of picture size. OptValue<OUString> moCropTop; ///< Specifies how much to crop the image from the top down as a fraction of picture size. OUString maLayoutFlowAlt; ///< Specifies the alternate layout flow for text in textboxes. + OUString maHyperlink; ///< The hyperlink assigned to the shape explicit ShapeTypeModel(); diff --git a/include/oox/vml/vmlshapecontext.hxx b/include/oox/vml/vmlshapecontext.hxx index ddca7b13ad93..0d4b3ddd9e7e 100644 --- a/include/oox/vml/vmlshapecontext.hxx +++ b/include/oox/vml/vmlshapecontext.hxx @@ -107,6 +107,8 @@ public: private: /** Processes the 'style' attribute. */ void setStyle( const OUString& rStyle ); + /** Processes the 'href' attribute. */ + void setHyperlink( const OUString& rHyperlink ); /** Resolve a relation identifier to a fragment path. */ OptValue< OUString > decodeFragmentPath( const AttributeList& rAttribs, sal_Int32 nToken ) const; diff --git a/include/svl/solar.hrc b/include/svl/solar.hrc index 521e24365f9f..5caeb64f40c2 100644 --- a/include/svl/solar.hrc +++ b/include/svl/solar.hrc @@ -23,7 +23,7 @@ // defines ------------------------------------------------------------------ #define OWN_ATTR_VALUE_START 3900 -#define OWN_ATTR_VALUE_END 4006 +#define OWN_ATTR_VALUE_END 4007 #define RID_LIB_START 10000 #define RID_LIB_END 19999 diff --git a/include/svx/svdobj.hxx b/include/svx/svdobj.hxx index 0960826d2c63..d7ccf830cf44 100644 --- a/include/svx/svdobj.hxx +++ b/include/svx/svdobj.hxx @@ -868,6 +868,9 @@ public: // If fillstyle is drawing::FillStyle_BITMAP, returns the graphic. const Graphic* getFillGraphic() const; + OUString getHyperlink() const { return msHyperlink; } + void setHyperlink(const OUString& sHyperlink) { msHyperlink = sHyperlink; } + protected: mutable tools::Rectangle m_aOutRect; // surrounding rectangle for Paint (incl. LineWidth, ...) Point m_aAnchor; // anchor position (Writer) @@ -971,6 +974,8 @@ private: // HACK: Do not automatically insert newly created object into a page. // The user needs to do it manually later. bool mbDoNotInsertIntoPageAutomatically; + // Hyperlink for the whole shape + OUString msHyperlink; // only for internal use! SvxShape* getSvxShape(); diff --git a/include/svx/unoshprp.hxx b/include/svx/unoshprp.hxx index 3f61ce77c6d1..750a245b29d4 100644 --- a/include/svx/unoshprp.hxx +++ b/include/svx/unoshprp.hxx @@ -195,7 +195,8 @@ #define OWN_ATTR_QRCODE (OWN_ATTR_VALUE_START+104) #define OWN_ATTR_TEXTFITTOSIZESCALE (OWN_ATTR_VALUE_START+105) #define OWN_ATTR_TEXTCOLUMNS (OWN_ATTR_VALUE_START+106) -// ATTENTION: current maximum is OWN_ATTR_VALUE_START+106 svx; wnen adding values, update +#define OWN_ATTR_HYPERLINK (OWN_ATTR_VALUE_START+107) +// ATTENTION: current maximum is OWN_ATTR_VALUE_START+107 svx; wnen adding values, update // OWN_ATTR_VALUE_END in include/svl/solar.hrc accordingly // #FontWork# @@ -358,7 +359,8 @@ { u"TextFitToSizeScale", OWN_ATTR_TEXTFITTOSIZESCALE, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, \ /* #i68101# */ \ { u"" UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , ::cppu::UnoType<OUString>::get(), 0, 0}, \ - { u"" UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"" UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , ::cppu::UnoType<OUString>::get(), 0, 0}, \ + { u"" UNO_NAME_HYPERLINK, OWN_ATTR_HYPERLINK, ::cppu::UnoType<OUString>::get(), 0, 0}, #define LINKTARGET_PROPERTIES \ { u"" UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0}, \ diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx index 77d3e1686d16..5b267184a291 100644 --- a/oox/source/export/vmlexport.cxx +++ b/oox/source/export/vmlexport.cxx @@ -1389,6 +1389,10 @@ sal_Int32 VMLExport::StartShape() break; } + if (!m_pSdrObject->getHyperlink().isEmpty()) + m_pShapeAttrList->add( + XML_href, OUStringToOString(m_pSdrObject->getHyperlink(), RTL_TEXTENCODING_UTF8)); + m_pShapeAttrList->addNS(XML_o, XML_allowincell, m_IsFollowingTextFlow ? "t" : "f"); // add style diff --git a/oox/source/token/properties.txt b/oox/source/token/properties.txt index 68b5e2d14af5..04b550e669ea 100644 --- a/oox/source/token/properties.txt +++ b/oox/source/token/properties.txt @@ -254,6 +254,7 @@ HoriOrientPosition HoriOrientRelation HorizontalSplitMode HorizontalSplitPositionTwips +Hyperlink IgnoreBlankCells IgnoreCase IgnoreLeadingSpaces diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx index 98481299e412..d1029173a1db 100644 --- a/oox/source/vml/vmlshape.cxx +++ b/oox/source/vml/vmlshape.cxx @@ -813,6 +813,10 @@ Reference< XShape > SimpleShape::implConvertAndInsert( const Reference< XShapes if(!maTypeModel.maWrapStyle.isEmpty()) PropertySet(xShape).setAnyProperty(PROP_TextWordWrap, makeAny(maTypeModel.maWrapStyle == "square")); + // tdf#123626 + if (!maTypeModel.maHyperlink.isEmpty()) + PropertySet(xShape).setAnyProperty(PROP_Hyperlink, makeAny(maTypeModel.maHyperlink)); + PropertySet(xShape).setAnyProperty(PROP_TextAutoGrowHeight, makeAny(maTypeModel.mbAutoHeight)); diff --git a/oox/source/vml/vmlshapecontext.cxx b/oox/source/vml/vmlshapecontext.cxx index 0d0c00b61d8c..d53a924c278a 100644 --- a/oox/source/vml/vmlshapecontext.cxx +++ b/oox/source/vml/vmlshapecontext.cxx @@ -312,6 +312,7 @@ ShapeTypeContext::ShapeTypeContext(ContextHandler2Helper const & rParent, mrTypeModel.moCoordPos = lclDecodeInt32Pair( rAttribs, XML_coordorigin ); mrTypeModel.moCoordSize = lclDecodeInt32Pair( rAttribs, XML_coordsize ); setStyle( rAttribs.getString( XML_style, OUString() ) ); + setHyperlink( rAttribs.getString( XML_href, OUString() ) ); if( lclDecodeBool( rAttribs, O_TOKEN( hr )).get( false )) { // MSO's handling of o:hr width is nowhere near what the spec says: // - o:hrpct is not in % but in 0.1% @@ -469,6 +470,11 @@ void ShapeTypeContext::setStyle( const OUString& rStyle ) } } +void ShapeTypeContext::setHyperlink( const OUString& rHyperlink ) +{ + mrTypeModel.maHyperlink = rHyperlink; +} + ShapeContext::ShapeContext(ContextHandler2Helper const& rParent, const std::shared_ptr<ShapeBase>& pShape, const AttributeList& rAttribs) : ShapeTypeContext(rParent, pShape, rAttribs) diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx index 28233c05fdd2..085d54dd1f07 100644 --- a/sc/source/filter/xml/xmlexprt.cxx +++ b/sc/source/filter/xml/xmlexprt.cxx @@ -3426,40 +3426,8 @@ void ScXMLExport::ExportShape(const uno::Reference < drawing::XShape >& xShape, } } if (!bIsChart) - { - OUString sHlink; - try - { - uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); - if ( xProps.is() ) - xProps->getPropertyValue( SC_UNONAME_HYPERLINK ) >>= sHlink; - } - catch ( const beans::UnknownPropertyException& ) - { - // no hyperlink property - } - - std::unique_ptr< SvXMLElementExport > pDrawA; - // enclose shapes with <draw:a> element only if sHlink contains something - if ( !sHlink.isEmpty() ) - { - // need to get delete the attributes that are pre-loaded - // for the shape export ( otherwise they will become - // attributes of the draw:a element ) This *shouldn't* - // affect performance adversely as there are only a - // couple of attributes involved - uno::Reference< xml::sax::XAttributeList > xSaveAttribs( new SvXMLAttributeList( GetAttrList() ) ); - ClearAttrList(); - // Add Hlink - AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); - AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sHlink); - pDrawA.reset( new SvXMLElementExport( *this, XML_NAMESPACE_DRAW, XML_A, false, false ) ); - // Attribute list has been cleared by previous operation - // re-add pre-loaded attributes - AddAttributeList( xSaveAttribs ); - } GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint); - } + IncrementProgressBar(false); } diff --git a/svx/source/unodraw/unoshape.cxx b/svx/source/unodraw/unoshape.cxx index 2a84ba1082c7..57fd337ed7af 100644 --- a/svx/source/unodraw/unoshape.cxx +++ b/svx/source/unodraw/unoshape.cxx @@ -2514,6 +2514,17 @@ bool SvxShape::setPropertyValueImpl( const OUString&, const SfxItemPropertyMapEn return true; } + case OWN_ATTR_HYPERLINK: + { + OUString sHyperlink; + if (rValue >>= sHyperlink) + { + GetSdrObject()->setHyperlink(sHyperlink); + return true; + } + break; + } + default: { return false; @@ -2935,6 +2946,12 @@ bool SvxShape::getPropertyValueImpl( const OUString&, const SfxItemPropertyMapEn break; } + case OWN_ATTR_HYPERLINK: + { + rValue <<= GetSdrObject()->getHyperlink(); + break; + } + default: return false; } diff --git a/sw/qa/extras/odfexport/data/shape-with-hyperlink.odt b/sw/qa/extras/odfexport/data/shape-with-hyperlink.odt Binary files differnew file mode 100644 index 000000000000..7b112782845f --- /dev/null +++ b/sw/qa/extras/odfexport/data/shape-with-hyperlink.odt diff --git a/sw/qa/extras/odfexport/odfexport2.cxx b/sw/qa/extras/odfexport/odfexport2.cxx index 5269779a1690..0812f2c801e3 100644 --- a/sw/qa/extras/odfexport/odfexport2.cxx +++ b/sw/qa/extras/odfexport/odfexport2.cxx @@ -82,6 +82,16 @@ DECLARE_ODFEXPORT_TEST(testListFormatDocx, "listformat.docx") "text:list-level-style-number[@text:level='3']", "num-suffix", "<<"); } +DECLARE_ODFEXPORT_TEST(testShapeWithHyperlink, "shape-with-hyperlink.odt") +{ + if (xmlDocUniquePtr pXmlDoc = parseExport("content.xml")) + { + // Check how conversion from prefix/suffix to list format did work + assertXPath(pXmlDoc, "/office:document-content/office:body/office:text/text:p/draw:a", + "href", "http://shape.com/"); + } +} + DECLARE_ODFEXPORT_TEST(testListFormatOdt, "listformat.odt") { // Ensure in resulting ODT we also have not just prefix/suffix, but custom delimiters diff --git a/sw/qa/extras/ooxmlexport/data/hyperlinkshape.docx b/sw/qa/extras/ooxmlexport/data/hyperlinkshape.docx Binary files differnew file mode 100644 index 000000000000..3bf4f8c5c498 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/hyperlinkshape.docx diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx index 7a1486f6a46c..37ed272b65b5 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx @@ -440,6 +440,13 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf142404_tabOverSpacingC15, "tdf142404_ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("8 lines high", 242*8, nHeight, 121); } +DECLARE_OOXMLEXPORT_TEST(testShapeHyperlink, "hyperlinkshape.docx") +{ + // Test import/export of hyperlink property on shapes + auto xShape(getShape(1)); + CPPUNIT_ASSERT_EQUAL(OUString("https://libreoffice.org/"), getProperty<OUString>(xShape, "Hyperlink")); +} + DECLARE_OOXMLEXPORT_TEST(testTdf139580, "tdf139580.odt") { // Without the fix in place, this test would have crashed at export time diff --git a/sw/sdi/_drwbase.sdi b/sw/sdi/_drwbase.sdi index eae02c15312c..e90d50cb92fb 100644 --- a/sw/sdi/_drwbase.sdi +++ b/sw/sdi/_drwbase.sdi @@ -117,5 +117,38 @@ interface BaseTextDrawBase StateMethod = GetState ; DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; ] + SID_EDIT_HYPERLINK + [ + ExecMethod = Execute ; + StateMethod = GetState ; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] + SID_HYPERLINK_DIALOG + [ + ExecMethod = Execute; + StateMethod = GetState; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] + SID_HYPERLINK_SETLINK // status() + [ + ExecMethod = Execute ; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] + SID_HYPERLINK_GETLINK // status() + [ + StateMethod = GetState ; + ] + SID_REMOVE_HYPERLINK + [ + ExecMethod = Execute ; + StateMethod = GetState; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] + SID_COPY_HYPERLINK_LOCATION + [ + ExecMethod = Execute ; + StateMethod = GetState; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] } diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx index 80554455fa3b..5a8fb68c284e 100644 --- a/sw/source/filter/ww8/docxsdrexport.cxx +++ b/sw/source/filter/ww8/docxsdrexport.cxx @@ -19,6 +19,7 @@ #include <svx/svdogrp.hxx> #include <svx/svdobjkind.hxx> #include <oox/token/namespaces.hxx> +#include <oox/token/relationship.hxx> #include <textboxhelper.hxx> #include <fmtanchr.hxx> #include <fmtsrnd.hxx> @@ -1269,7 +1270,20 @@ void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFo && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) pDocPrAttrList->add(XML_hidden, OString::number(1).getStr()); - pFS->singleElementNS(XML_wp, XML_docPr, pDocPrAttrList); + + pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList); + OUString sHyperlink = pSdrObject->getHyperlink(); + if (!sHyperlink.isEmpty()) + { + OUString sRelId = m_pImpl->getExport().GetFilter().addRelation( + pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK), + oox::drawingml::URLTransformer().getTransformedString(sHyperlink), + oox::drawingml::URLTransformer().isExternalURL(sHyperlink)); + pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, + FSNS(XML_xmlns, XML_a), + m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml))); + } + pFS->endElementNS(XML_wp, XML_docPr); uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"; diff --git a/sw/source/uibase/shells/drwbassh.cxx b/sw/source/uibase/shells/drwbassh.cxx index 6d8ba4159d37..29b7ec39a2a6 100644 --- a/sw/source/uibase/shells/drwbassh.cxx +++ b/sw/source/uibase/shells/drwbassh.cxx @@ -27,6 +27,7 @@ #include <svl/whiter.hxx> #include <svx/swframevalidation.hxx> #include <svx/anchorid.hxx> +#include <svx/hlnkitem.hxx> #include <osl/diagnose.h> #include <drawdoc.hxx> #include <uitool.hxx> @@ -47,16 +48,23 @@ #include <sfx2/msg.hxx> #include <swslots.hxx> #include <svx/svxdlg.hxx> +#include <vcl/unohelp2.hxx> #include <swabstdlg.hxx> #include <swundo.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XShape.hpp> #include <com/sun/star/text/HoriOrientation.hpp> #include <com/sun/star/text/VertOrientation.hpp> #include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/uno/Reference.hxx> #include <IDocumentDrawModelAccess.hxx> #include <fmtfollowtextflow.hxx> #include <textboxhelper.hxx> using namespace ::com::sun::star; +using namespace css::beans; +using namespace css::drawing; +using namespace css::uno; SFX_IMPL_SUPERCLASS_INTERFACE(SwDrawBaseShell, SwBaseShell) @@ -597,6 +605,43 @@ void SwDrawBaseShell::Execute(SfxRequest const &rReq) break; } + case SID_EDIT_HYPERLINK: + case SID_HYPERLINK_DIALOG: + { + GetView().GetViewFrame()->SetChildWindow(SID_HYPERLINK_DIALOG, true); + break; + } + + case SID_HYPERLINK_SETLINK: + { + if(pItem) + { + const SvxHyperlinkItem& rHLinkItem = *static_cast<const SvxHyperlinkItem *>(pItem); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + pObj->setHyperlink(rHLinkItem.GetURL()); + } + break; + } + + case SID_REMOVE_HYPERLINK: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + pObj->setHyperlink(OUString()); + break; + } + + case SID_COPY_HYPERLINK_LOCATION: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + uno::Reference<datatransfer::clipboard::XClipboard> xClipboard + = GetView().GetEditWin().GetClipboard(); + vcl::unohelper::TextDataObject::CopyStringTo(pObj->getHyperlink(), xClipboard); + break; + } + default: OSL_ENSURE(false, "wrong Dispatcher"); return; @@ -791,6 +836,35 @@ void SwDrawBaseShell::GetState(SfxItemSet& rSet) } } break; + + case SID_EDIT_HYPERLINK: + case SID_HYPERLINK_DIALOG: + case SID_REMOVE_HYPERLINK: + case SID_COPY_HYPERLINK_LOCATION: + { + if (pSdrView->GetMarkedObjectCount() != 1) + rSet.DisableItem(nWhich); + else if (nWhich == SID_REMOVE_HYPERLINK || nWhich == SID_EDIT_HYPERLINK + || nWhich == SID_COPY_HYPERLINK_LOCATION) + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if (pObj->getHyperlink().isEmpty()) + rSet.DisableItem(nWhich); + } + } + break; + + case SID_HYPERLINK_GETLINK: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + OUString sHyperLink = pObj->getHyperlink(); + SvxHyperlinkItem aHLinkItem; + aHLinkItem.SetURL(sHyperLink); + rSet.Put(aHLinkItem); + } + break; } nWhich = aIter.NextWhich(); } diff --git a/sw/uiconfig/sglobal/popupmenu/draw.xml b/sw/uiconfig/sglobal/popupmenu/draw.xml index d119c1add5cf..799c02cc5e2e 100644 --- a/sw/uiconfig/sglobal/popupmenu/draw.xml +++ b/sw/uiconfig/sglobal/popupmenu/draw.xml @@ -71,6 +71,11 @@ <menu:menuitem menu:id=".uno:EnterGroup"/> <menu:menuitem menu:id=".uno:LeaveGroup"/> <menu:menuseparator/> + <menu:menuitem menu:id=".uno:OpenHyperlinkOnCursor"/> + <menu:menuitem menu:id=".uno:EditHyperlink"/> + <menu:menuitem menu:id=".uno:CopyHyperlinkLocation"/> + <menu:menuitem menu:id=".uno:RemoveHyperlink"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:AddTextBox"/> <menu:menuitem menu:id=".uno:RemoveTextBox"/> <menu:menuitem menu:id=".uno:EditSignatureLine"/> diff --git a/sw/uiconfig/swform/popupmenu/draw.xml b/sw/uiconfig/swform/popupmenu/draw.xml index d119c1add5cf..799c02cc5e2e 100644 --- a/sw/uiconfig/swform/popupmenu/draw.xml +++ b/sw/uiconfig/swform/popupmenu/draw.xml @@ -71,6 +71,11 @@ <menu:menuitem menu:id=".uno:EnterGroup"/> <menu:menuitem menu:id=".uno:LeaveGroup"/> <menu:menuseparator/> + <menu:menuitem menu:id=".uno:OpenHyperlinkOnCursor"/> + <menu:menuitem menu:id=".uno:EditHyperlink"/> + <menu:menuitem menu:id=".uno:CopyHyperlinkLocation"/> + <menu:menuitem menu:id=".uno:RemoveHyperlink"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:AddTextBox"/> <menu:menuitem menu:id=".uno:RemoveTextBox"/> <menu:menuitem menu:id=".uno:EditSignatureLine"/> diff --git a/sw/uiconfig/swreport/popupmenu/draw.xml b/sw/uiconfig/swreport/popupmenu/draw.xml index d119c1add5cf..799c02cc5e2e 100644 --- a/sw/uiconfig/swreport/popupmenu/draw.xml +++ b/sw/uiconfig/swreport/popupmenu/draw.xml @@ -71,6 +71,11 @@ <menu:menuitem menu:id=".uno:EnterGroup"/> <menu:menuitem menu:id=".uno:LeaveGroup"/> <menu:menuseparator/> + <menu:menuitem menu:id=".uno:OpenHyperlinkOnCursor"/> + <menu:menuitem menu:id=".uno:EditHyperlink"/> + <menu:menuitem menu:id=".uno:CopyHyperlinkLocation"/> + <menu:menuitem menu:id=".uno:RemoveHyperlink"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:AddTextBox"/> <menu:menuitem menu:id=".uno:RemoveTextBox"/> <menu:menuitem menu:id=".uno:EditSignatureLine"/> diff --git a/sw/uiconfig/swriter/popupmenu/draw.xml b/sw/uiconfig/swriter/popupmenu/draw.xml index d119c1add5cf..799c02cc5e2e 100644 --- a/sw/uiconfig/swriter/popupmenu/draw.xml +++ b/sw/uiconfig/swriter/popupmenu/draw.xml @@ -71,6 +71,11 @@ <menu:menuitem menu:id=".uno:EnterGroup"/> <menu:menuitem menu:id=".uno:LeaveGroup"/> <menu:menuseparator/> + <menu:menuitem menu:id=".uno:OpenHyperlinkOnCursor"/> + <menu:menuitem menu:id=".uno:EditHyperlink"/> + <menu:menuitem menu:id=".uno:CopyHyperlinkLocation"/> + <menu:menuitem menu:id=".uno:RemoveHyperlink"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:AddTextBox"/> <menu:menuitem menu:id=".uno:RemoveTextBox"/> <menu:menuitem menu:id=".uno:EditSignatureLine"/> diff --git a/sw/uiconfig/swxform/popupmenu/draw.xml b/sw/uiconfig/swxform/popupmenu/draw.xml index d119c1add5cf..799c02cc5e2e 100644 --- a/sw/uiconfig/swxform/popupmenu/draw.xml +++ b/sw/uiconfig/swxform/popupmenu/draw.xml @@ -71,6 +71,11 @@ <menu:menuitem menu:id=".uno:EnterGroup"/> <menu:menuitem menu:id=".uno:LeaveGroup"/> <menu:menuseparator/> + <menu:menuitem menu:id=".uno:OpenHyperlinkOnCursor"/> + <menu:menuitem menu:id=".uno:EditHyperlink"/> + <menu:menuitem menu:id=".uno:CopyHyperlinkLocation"/> + <menu:menuitem menu:id=".uno:RemoveHyperlink"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:AddTextBox"/> <menu:menuitem menu:id=".uno:RemoveTextBox"/> <menu:menuitem menu:id=".uno:EditSignatureLine"/> diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx index 55a4eb6e1ec8..940780ca9705 100644 --- a/writerfilter/source/dmapper/GraphicImport.cxx +++ b/writerfilter/source/dmapper/GraphicImport.cxx @@ -408,9 +408,6 @@ public: uno::Reference< container::XNamed > xNamed( xGraphicObjectProperties, uno::UNO_QUERY_THROW ); xNamed->setName(rDomainMapper.GetGraphicNamingHelper().NameGraphic(sName)); - if ( sHyperlinkURL.getLength() > 0 ) - xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HYPER_LINK_U_R_L ), - uno::makeAny ( sHyperlinkURL )); xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_DESCRIPTION ), uno::makeAny( sAlternativeText )); xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TITLE ), @@ -422,6 +419,17 @@ public: } } + void applyHyperlink(uno::Reference<beans::XPropertySet> const & xShapeProps, bool bIsShape) + { + // Graphic objects have a different hyperlink prop than shapes + auto aHyperlinkProp = bIsShape ? PROP_HYPERLINK : PROP_HYPER_LINK_U_R_L; + if (!sHyperlinkURL.isEmpty()) + { + xShapeProps->setPropertyValue( + getPropertyName(aHyperlinkProp), uno::makeAny(sHyperlinkURL)); + } + } + /// Getter for m_aInteropGrabBag, but also merges in the values from other members if they are set. comphelper::SequenceAsHashMap const & getInteropGrabBag() { @@ -1271,6 +1279,7 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue) xShapeProps->setPropertyValue("Surround", uno::makeAny(static_cast<sal_Int32>(m_pImpl->nWrap))); m_pImpl->applyZOrder(xShapeProps); m_pImpl->applyName(xShapeProps); + m_pImpl->applyHyperlink(xShapeProps, bUseShape); xShapeProps->setPropertyValue("AllowOverlap", uno::makeAny(m_pImpl->bAllowOverlap)); @@ -1858,6 +1867,7 @@ uno::Reference<text::XTextContent> GraphicImport::createGraphicObject(uno::Refer uno::makeAny( awt::Size( m_pImpl->getXSize(), m_pImpl->getYSize() ))); m_pImpl->applyMargins(xGraphicObjectProperties); m_pImpl->applyName(xGraphicObjectProperties); + m_pImpl->applyHyperlink(xGraphicObjectProperties, false); } // Handle horizontal flip. diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx index 0100313bdf45..c3e7cbaa2de9 100644 --- a/writerfilter/source/dmapper/PropertyIds.cxx +++ b/writerfilter/source/dmapper/PropertyIds.cxx @@ -127,6 +127,7 @@ OUString getPropertyName( PropertyIds eId ) case PROP_SUB_TYPE : sName = "SubType"; break; case PROP_FILE_FORMAT : sName = "FileFormat"; break; case PROP_HYPER_LINK_U_R_L : sName = "HyperLinkURL"; break; + case PROP_HYPERLINK : sName = "Hyperlink"; break; case PROP_NUMBER_FORMAT : sName = "NumberFormat"; break; case PROP_NAME : sName = "Name"; break; case PROP_IS_INPUT : sName = "IsInput"; break; diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx index a6afe0c5313f..f1bef16da581 100644 --- a/writerfilter/source/dmapper/PropertyIds.hxx +++ b/writerfilter/source/dmapper/PropertyIds.hxx @@ -185,6 +185,7 @@ enum PropertyIds ,PROP_HORI_ORIENT_POSITION ,PROP_HORI_ORIENT_RELATION ,PROP_HYPER_LINK_U_R_L + ,PROP_HYPERLINK ,PROP_INDENT_AT ,PROP_INPUT_STREAM ,PROP_INSERT diff --git a/xmloff/source/draw/shapeexport.cxx b/xmloff/source/draw/shapeexport.cxx index 105281b46d0c..2c757e2b2237 100644 --- a/xmloff/source/draw/shapeexport.cxx +++ b/xmloff/source/draw/shapeexport.cxx @@ -167,6 +167,7 @@ constexpr OUStringLiteral gsVerb( u"Verb" ); constexpr OUStringLiteral gsSoundURL( u"SoundURL" ); constexpr OUStringLiteral gsSpeed( u"Speed" ); constexpr OUStringLiteral gsStarBasic( u"StarBasic" ); +constexpr OUStringLiteral gsHyperlink( u"Hyperlink" ); XMLShapeExport::XMLShapeExport(SvXMLExport& rExp, SvXMLExportPropertyMapper *pExtMapper ) @@ -578,17 +579,30 @@ void XMLShapeExport::exportShape(const uno::Reference< drawing::XShape >& xShape } sal_Int32 nZIndex = 0; uno::Reference< beans::XPropertySet > xSet( xShape, uno::UNO_QUERY ); + OUString sHyperlink; + try + { + xSet->getPropertyValue(gsHyperlink) >>= sHyperlink; + } + catch (beans::UnknownPropertyException) + { + } std::unique_ptr< SvXMLElementExport > pHyperlinkElement; - // export hyperlinks with <a><shape/></a>. Currently only in draw since draw - // does not support document events + // Need to stash the attributes that are pre-loaded for the shape export + // (otherwise they will become attributes of the draw:a element) + uno::Reference<xml::sax::XAttributeList> xSaveAttribs( + new SvXMLAttributeList(GetExport().GetAttrList())); + GetExport().ClearAttrList(); if( xSet.is() && (GetExport().GetModelType() == SvtModuleOptions::EFactory::DRAW) ) { + // export hyperlinks with <a><shape/></a>. Currently only in draw since draw + // does not support document events try { presentation::ClickAction eAction = presentation::ClickAction_NONE; - xSet->getPropertyValue("OnClick") >>= eAction; + xSet->getPropertyValue(gsOnClick) >>= eAction; if( (eAction == presentation::ClickAction_DOCUMENT) || (eAction == presentation::ClickAction_BOOKMARK) ) @@ -610,6 +624,14 @@ void XMLShapeExport::exportShape(const uno::Reference< drawing::XShape >& xShape TOOLS_WARN_EXCEPTION("xmloff", "XMLShapeExport::exportShape(): exception during hyperlink export"); } } + else if (xSet.is() && !sHyperlink.isEmpty()) + { + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sHyperlink ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + pHyperlinkElement.reset( new SvXMLElementExport(mrExport, XML_NAMESPACE_DRAW, XML_A, true, true) ); + } + // re-add stashed attributes + GetExport().AddAttributeList(xSaveAttribs); if( xSet.is() ) xSet->getPropertyValue(gsZIndex) >>= nZIndex; diff --git a/xmloff/source/text/XMLTextFrameContext.cxx b/xmloff/source/text/XMLTextFrameContext.cxx index 39bbf597f9a5..a4f8cb636ca1 100644 --- a/xmloff/source/text/XMLTextFrameContext.cxx +++ b/xmloff/source/text/XMLTextFrameContext.cxx @@ -1648,6 +1648,10 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTextFrameContext::c } xContext = &dynamic_cast<SvXMLImportContext&>(*m_xImplContext->createFastChildContext(nElement, xAttrList)); } + else if (nElement == XML_ELEMENT(DRAW, XML_A)) + { + xContext = &dynamic_cast<SvXMLImportContext&>(*m_xImplContext->createFastChildContext(nElement, xAttrList)); + } else { // the child is a drawing shape diff --git a/xmloff/source/text/XMLTextFrameHyperlinkContext.cxx b/xmloff/source/text/XMLTextFrameHyperlinkContext.cxx index 240d9776b519..f3bf9c66665e 100644 --- a/xmloff/source/text/XMLTextFrameHyperlinkContext.cxx +++ b/xmloff/source/text/XMLTextFrameHyperlinkContext.cxx @@ -20,6 +20,7 @@ #include <sal/log.hxx> #include <sax/tools/converter.hxx> +#include <xmloff/shapeimport.hxx> #include <xmloff/xmlimp.hxx> #include <xmloff/namespacemap.hxx> #include <xmloff/xmlnamespace.hxx> @@ -27,6 +28,9 @@ #include "XMLTextFrameContext.hxx" #include "XMLTextFrameHyperlinkContext.hxx" +#include <com/sun/star/drawing/XShapes.hpp> + +using namespace ::com::sun::star::drawing; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::text; using namespace ::com::sun::star::xml::sax; @@ -106,6 +110,14 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTextFrameHyperlinkC pContext = pTextFrameContext; xFrameContext = pContext; } + if (nElement == XML_ELEMENT(DRAW, XML_CUSTOM_SHAPE)) + { + Reference<XShapes> xShapes; + SvXMLShapeContext* pShapeContext + = XMLShapeImportHelper::CreateGroupChildContext(GetImport(), nElement, xAttrList, xShapes); + pShapeContext->setHyperlink(sHRef); + pContext = pShapeContext; + } if (!pContext) XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); diff --git a/xmloff/source/text/XMLTextFrameHyperlinkContext.hxx b/xmloff/source/text/XMLTextFrameHyperlinkContext.hxx index 3b178bb6f835..a76081632a9f 100644 --- a/xmloff/source/text/XMLTextFrameHyperlinkContext.hxx +++ b/xmloff/source/text/XMLTextFrameHyperlinkContext.hxx @@ -29,6 +29,7 @@ namespace com::sun::star { namespace beans { class XPropertySet; } } +/// Used for hyperlinks attached to objects (drawing objects, text boxes, Writer frames) class XMLTextFrameHyperlinkContext : public SvXMLImportContext { OUString sHRef; |