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