xref: /core/sc/source/ui/vba/vbahyperlinks.cxx (revision 5a602ed852b872f87893169062ec47041e013fb9)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "vbahyperlinks.hxx"
21 #include <algorithm>
22 #include <vector>
23 #include <cppuhelper/implbase.hxx>
24 #include <ooo/vba/office/MsoHyperlinkType.hpp>
25 #include <rangelst.hxx>
26 #include "vbahyperlink.hxx"
27 #include "vbarange.hxx"
28 
29 using namespace ::ooo::vba;
30 using namespace ::com::sun::star;
31 
32 namespace {
33 
34 /** Returns true, if every range of rxInner is contained in any range of rScOuter.
35 
36     @throws css::uno::RuntimeException
37 */
lclContains(const ScRangeList & rScOuter,const uno::Reference<excel::XRange> & rxInner)38 bool lclContains( const ScRangeList& rScOuter, const uno::Reference< excel::XRange >& rxInner )
39 {
40     const ScRangeList& rScInner = ScVbaRange::getScRangeList( rxInner );
41     if( rScInner.empty() || rScOuter.empty() )
42         throw uno::RuntimeException(u"Empty range objects"_ustr );
43 
44     for( size_t nIndex = 0, nCount = rScInner.size(); nIndex < nCount; ++nIndex )
45         if( !rScOuter.Contains( rScInner[ nIndex ] ) )
46             return false;
47     return true;
48 }
49 
50 /** Functor to decide whether the anchors of two Hyperlink objects are equal. */
51 struct EqualAnchorFunctor
52 {
53     uno::Reference< excel::XRange > mxAnchorRange;
54     uno::Reference< msforms::XShape > mxAnchorShape;
55     sal_Int32 mnType;
56     /// @throws uno::RuntimeException
57     explicit EqualAnchorFunctor( const uno::Reference< excel::XHyperlink >& rxHlink );
58     /// @throws uno::RuntimeException
59     bool operator()( const uno::Reference< excel::XHyperlink >& rxHlink ) const;
60 };
61 
EqualAnchorFunctor(const uno::Reference<excel::XHyperlink> & rxHlink)62 EqualAnchorFunctor::EqualAnchorFunctor( const uno::Reference< excel::XHyperlink >& rxHlink ) :
63     mnType( rxHlink->getType() )
64 {
65     switch( mnType )
66     {
67         case office::MsoHyperlinkType::msoHyperlinkRange:
68             mxAnchorRange.set( rxHlink->getRange(), uno::UNO_SET_THROW );
69         break;
70         case office::MsoHyperlinkType::msoHyperlinkShape:
71         case office::MsoHyperlinkType::msoHyperlinkInlineShape:
72             mxAnchorShape.set( rxHlink->getShape(), uno::UNO_SET_THROW );
73         break;
74         default:
75             throw uno::RuntimeException();
76     }
77 }
78 
operator ()(const uno::Reference<excel::XHyperlink> & rxHlink) const79 bool EqualAnchorFunctor::operator()( const uno::Reference< excel::XHyperlink >& rxHlink ) const
80 {
81     sal_Int32 nType = rxHlink->getType();
82     if( nType != mnType )
83         return false;
84 
85     switch( nType )
86     {
87         case office::MsoHyperlinkType::msoHyperlinkRange:
88         {
89             uno::Reference< excel::XRange > xAnchorRange( rxHlink->getRange(), uno::UNO_SET_THROW );
90             const ScRangeList& rScRanges1 = ScVbaRange::getScRangeList( xAnchorRange );
91             const ScRangeList& rScRanges2 = ScVbaRange::getScRangeList( mxAnchorRange );
92             return (rScRanges1.size() == 1) && (rScRanges2.size() == 1) && (rScRanges1[ 0 ] == rScRanges2[ 0 ]);
93         }
94         case office::MsoHyperlinkType::msoHyperlinkShape:
95         case office::MsoHyperlinkType::msoHyperlinkInlineShape:
96         {
97             uno::Reference< msforms::XShape > xAnchorShape( rxHlink->getShape(), uno::UNO_SET_THROW );
98             return xAnchorShape.get() == mxAnchorShape.get();
99         }
100         default:
101             throw uno::RuntimeException();
102     }
103 }
104 
105 } // namespace
106 
107 namespace detail {
108 
109 class ScVbaHlinkContainer : public ::cppu::WeakImplHelper< container::XIndexAccess >
110 {
111 public:
112     /// @throws uno::RuntimeException
113     explicit ScVbaHlinkContainer();
114     /// @throws uno::RuntimeException
115     explicit ScVbaHlinkContainer( const ScVbaHlinkContainerRef& rxSheetContainer, const ScRangeList& rScRanges );
116 
117     /** Inserts the passed hyperlink into the collection. Will remove a
118         Hyperlink object with the same anchor as the passed Hyperlink object.
119 
120         @throws uno::RuntimeException
121     */
122     void insertHyperlink( const uno::Reference< excel::XHyperlink >& rxHlink );
123 
124     // XIndexAccess
125     virtual sal_Int32 SAL_CALL getCount() override;
126     virtual uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override;
127 
128     // XElementAccess
129     virtual uno::Type SAL_CALL getElementType() override;
130     virtual sal_Bool SAL_CALL hasElements() override;
131 
132 private:
133     typedef ::std::vector< uno::Reference< excel::XHyperlink > > HyperlinkVector;
134     HyperlinkVector     maHlinks;
135 };
136 
ScVbaHlinkContainer()137 ScVbaHlinkContainer::ScVbaHlinkContainer()
138 {
139     // TODO FIXME: fill with existing hyperlinks
140 }
141 
ScVbaHlinkContainer(const ScVbaHlinkContainerRef & rxSheetContainer,const ScRangeList & rScRanges)142 ScVbaHlinkContainer::ScVbaHlinkContainer( const ScVbaHlinkContainerRef& rxSheetContainer,
143         const ScRangeList& rScRanges )
144 {
145     for( sal_Int32 nIndex = 0, nCount = rxSheetContainer->getCount(); nIndex < nCount; ++nIndex )
146     {
147         uno::Reference< excel::XHyperlink > xHlink( rxSheetContainer->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
148         uno::Reference< excel::XRange > xHlinkRange( xHlink->getRange(), uno::UNO_SET_THROW );
149         if( lclContains( rScRanges, xHlinkRange ) )
150             maHlinks.push_back( xHlink );
151     }
152 }
153 
insertHyperlink(const uno::Reference<excel::XHyperlink> & rxHlink)154 void ScVbaHlinkContainer::insertHyperlink( const uno::Reference< excel::XHyperlink >& rxHlink )
155 {
156     HyperlinkVector::iterator aIt = ::std::find_if( maHlinks.begin(), maHlinks.end(), EqualAnchorFunctor( rxHlink ) );
157     if( aIt == maHlinks.end() )
158         maHlinks.push_back( rxHlink );
159     else
160         *aIt = rxHlink;
161 }
162 
getCount()163 sal_Int32 SAL_CALL ScVbaHlinkContainer::getCount()
164 {
165     return static_cast< sal_Int32 >( maHlinks.size() );
166 }
167 
getByIndex(sal_Int32 nIndex)168 uno::Any SAL_CALL ScVbaHlinkContainer::getByIndex( sal_Int32 nIndex )
169 {
170     if( (0 <= nIndex) && (nIndex < getCount()) )
171         return uno::Any( maHlinks[ static_cast< size_t >( nIndex ) ] );
172     throw lang::IndexOutOfBoundsException();
173 }
174 
getElementType()175 uno::Type SAL_CALL ScVbaHlinkContainer::getElementType()
176 {
177     return cppu::UnoType<excel::XHyperlink>::get();
178 }
179 
hasElements()180 sal_Bool SAL_CALL ScVbaHlinkContainer::hasElements()
181 {
182     return !maHlinks.empty();
183 }
184 
ScVbaHlinkContainerMember(ScVbaHlinkContainer * pContainer)185 ScVbaHlinkContainerMember::ScVbaHlinkContainerMember( ScVbaHlinkContainer* pContainer ) :
186     mxContainer( pContainer )
187 {
188 }
189 
~ScVbaHlinkContainerMember()190 ScVbaHlinkContainerMember::~ScVbaHlinkContainerMember()
191 {
192 }
193 
194 } // namespace detail
195 
ScVbaHyperlinks(const uno::Reference<XHelperInterface> & rxParent,const uno::Reference<uno::XComponentContext> & rxContext)196 ScVbaHyperlinks::ScVbaHyperlinks( const uno::Reference< XHelperInterface >& rxParent,
197         const uno::Reference< uno::XComponentContext >& rxContext ) :
198     detail::ScVbaHlinkContainerMember( new detail::ScVbaHlinkContainer ),
199     ScVbaHyperlinks_BASE( rxParent, rxContext, uno::Reference< container::XIndexAccess >( mxContainer ) )
200 {
201 }
202 
ScVbaHyperlinks(const uno::Reference<XHelperInterface> & rxParent,const uno::Reference<uno::XComponentContext> & rxContext,const ScVbaHyperlinksRef & rxSheetHlinks,const ScRangeList & rScRanges)203 ScVbaHyperlinks::ScVbaHyperlinks( const uno::Reference< XHelperInterface >& rxParent,
204         const uno::Reference< uno::XComponentContext >& rxContext,
205         const ScVbaHyperlinksRef& rxSheetHlinks, const ScRangeList& rScRanges ) :
206     detail::ScVbaHlinkContainerMember( new detail::ScVbaHlinkContainer( rxSheetHlinks->mxContainer, rScRanges ) ),
207     ScVbaHyperlinks_BASE( rxParent, rxContext, uno::Reference< container::XIndexAccess >( mxContainer ) ),
208     mxSheetHlinks( rxSheetHlinks )
209 {
210 }
211 
~ScVbaHyperlinks()212 ScVbaHyperlinks::~ScVbaHyperlinks()
213 {
214 }
215 
216 // XHyperlinks ----------------------------------------------------------------
217 
Add(const uno::Any & rAnchor,const uno::Any & rAddress,const uno::Any & rSubAddress,const uno::Any & rScreenTip,const uno::Any & rTextToDisplay)218 uno::Reference< excel::XHyperlink > SAL_CALL ScVbaHyperlinks::Add(
219     const uno::Any& rAnchor, const uno::Any& rAddress, const uno::Any& rSubAddress,
220     const uno::Any& rScreenTip, const uno::Any& rTextToDisplay )
221 {
222     /*  If this Hyperlinks object has been created from a Range object, the
223         call to Add() is passed to the Hyperlinks object of the parent
224         worksheet. This container will not be modified (it will not contain the
225         inserted hyperlink).
226         For details, see documentation in hyperlinks.hxx.
227      */
228     if( mxSheetHlinks.is() )
229         return mxSheetHlinks->Add( rAnchor, rAddress, rSubAddress, rScreenTip, rTextToDisplay );
230 
231     // get anchor object (can be a Range or a Shape object)
232     uno::Reference< XHelperInterface > xAnchor( rAnchor, uno::UNO_QUERY_THROW );
233 
234     /*  Create the Hyperlink object, this tries to insert the hyperlink into
235         the spreadsheet document. Parent of the Hyperlink is the anchor object. */
236     uno::Reference< excel::XHyperlink > xHlink( new ScVbaHyperlink(
237         xAnchor, mxContext, rAddress, rSubAddress, rScreenTip, rTextToDisplay ) );
238 
239     /*  If creation of the hyperlink did not throw, insert it into the
240         collection. */
241     mxContainer->insertHyperlink( xHlink );
242     return xHlink;
243 }
244 
Delete()245 void SAL_CALL ScVbaHyperlinks::Delete()
246 {
247     // FIXME not implemented
248     throw uno::RuntimeException();
249 }
250 
251 // XEnumerationAccess ---------------------------------------------------------
252 
createEnumeration()253 uno::Reference< container::XEnumeration > SAL_CALL ScVbaHyperlinks::createEnumeration()
254 {
255     return new SimpleIndexAccessToEnumeration( m_xIndexAccess );
256 }
257 
258 // XElementAccess -------------------------------------------------------------
259 
getElementType()260 uno::Type SAL_CALL ScVbaHyperlinks::getElementType()
261 {
262     return cppu::UnoType<excel::XHyperlink>::get();
263 }
264 
265 // ScVbaCollectionBase --------------------------------------------------------
266 
createCollectionObject(const uno::Any & rSource)267 uno::Any ScVbaHyperlinks::createCollectionObject( const uno::Any& rSource )
268 {
269     // container stores XHyperlink objects, just return the passed object
270     return rSource;
271 }
272 
273 // XHelperInterface -----------------------------------------------------------
274 
275 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaHyperlinks, u"ooo.vba.excel.Hyperlinks"_ustr )
276 
277 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
278