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 <sal/config.h>
21
22 #include <comphelper/interfacecontainer2.hxx>
23 #include <comphelper/sequence.hxx>
24 #include <comphelper/servicehelper.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <svl/listener.hxx>
27 #include <osl/mutex.hxx>
28
29 #include <unofootnote.hxx>
30 #include <unotextrange.hxx>
31 #include <unotextcursor.hxx>
32 #include <unoparagraph.hxx>
33 #include <unomap.hxx>
34 #include <unoprnms.hxx>
35 #include <doc.hxx>
36 #include <ftnidx.hxx>
37 #include <fmtftn.hxx>
38 #include <txtftn.hxx>
39 #include <ndtxt.hxx>
40 #include <unocrsr.hxx>
41 #include <svl/itemprop.hxx>
42
43 using namespace ::com::sun::star;
44
45 namespace {
46
47 uno::Sequence< OUString >
GetSupportedServiceNamesImpl(size_t const nServices,char const * const pServices[])48 GetSupportedServiceNamesImpl(
49 size_t const nServices, char const*const pServices[])
50 {
51 uno::Sequence< OUString > ret(static_cast<sal_Int32>(nServices));
52
53 std::transform(pServices, pServices + nServices, ret.begin(),
54 [](const char* pService) -> OUString { return OUString::createFromAscii(pService); });
55
56 return ret;
57 }
58
59 }
60
61 class SwXFootnote::Impl
62 : public SvtListener
63 {
64 private:
65 ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2
66
67 public:
68
69 SwXFootnote& m_rThis;
70 uno::WeakReference<uno::XInterface> m_wThis;
71 const bool m_bIsEndnote;
72 ::comphelper::OInterfaceContainerHelper2 m_EventListeners;
73 bool m_bIsDescriptor;
74 SwFormatFootnote* m_pFormatFootnote;
75 OUString m_sLabel;
76
Impl(SwXFootnote & rThis,SwFormatFootnote * const pFootnote,const bool bIsEndnote)77 Impl(SwXFootnote& rThis,
78 SwFormatFootnote* const pFootnote,
79 const bool bIsEndnote)
80 : m_rThis(rThis)
81 , m_bIsEndnote(bIsEndnote)
82 , m_EventListeners(m_Mutex)
83 , m_bIsDescriptor(nullptr == pFootnote)
84 , m_pFormatFootnote(pFootnote)
85 {
86 m_pFormatFootnote && StartListening(m_pFormatFootnote->GetNotifier());
87 }
88
GetFootnoteFormat() const89 const SwFormatFootnote* GetFootnoteFormat() const {
90 return m_rThis.GetDoc() ? m_pFormatFootnote : nullptr;
91 }
92
GetFootnoteFormatOrThrow() const93 SwFormatFootnote const& GetFootnoteFormatOrThrow() const {
94 SwFormatFootnote const*const pFootnote( GetFootnoteFormat() );
95 if (!pFootnote) {
96 throw uno::RuntimeException("SwXFootnote: disposed or invalid", nullptr);
97 }
98 return *pFootnote;
99 }
100
101 void Invalidate();
102 protected:
103 void Notify(const SfxHint& rHint) override;
104
105 };
106
Invalidate()107 void SwXFootnote::Impl::Invalidate()
108 {
109 EndListeningAll();
110 m_pFormatFootnote = nullptr;
111 m_rThis.SetDoc(nullptr);
112 uno::Reference<uno::XInterface> const xThis(m_wThis);
113 if (!xThis.is())
114 { // fdo#72695: if UNO object is already dead, don't revive it with event
115 return;
116 }
117 lang::EventObject const ev(xThis);
118 m_EventListeners.disposeAndClear(ev);
119 }
120
Notify(const SfxHint & rHint)121 void SwXFootnote::Impl::Notify(const SfxHint& rHint)
122 {
123 if(rHint.GetId() == SfxHintId::Dying)
124 Invalidate();
125 }
126
SwXFootnote(const bool bEndnote)127 SwXFootnote::SwXFootnote(const bool bEndnote)
128 : SwXText(nullptr, CursorType::Footnote)
129 , m_pImpl( new SwXFootnote::Impl(*this, nullptr, bEndnote) )
130 {
131 }
132
SwXFootnote(SwDoc & rDoc,SwFormatFootnote & rFormat)133 SwXFootnote::SwXFootnote(SwDoc & rDoc, SwFormatFootnote & rFormat)
134 : SwXText(& rDoc, CursorType::Footnote)
135 , m_pImpl( new SwXFootnote::Impl(*this, &rFormat, rFormat.IsEndNote()) )
136 {
137 }
138
~SwXFootnote()139 SwXFootnote::~SwXFootnote()
140 {
141 }
142
143 uno::Reference<text::XFootnote>
CreateXFootnote(SwDoc & rDoc,SwFormatFootnote * const pFootnoteFormat,bool const isEndnote)144 SwXFootnote::CreateXFootnote(SwDoc & rDoc, SwFormatFootnote *const pFootnoteFormat,
145 bool const isEndnote)
146 {
147 // i#105557: do not iterate over the registered clients: race condition
148 uno::Reference<text::XFootnote> xNote;
149 if (pFootnoteFormat)
150 {
151 xNote = pFootnoteFormat->GetXFootnote();
152 }
153 if (!xNote.is())
154 {
155 SwXFootnote *const pNote(pFootnoteFormat
156 ? new SwXFootnote(rDoc, *pFootnoteFormat)
157 : new SwXFootnote(isEndnote));
158 xNote.set(pNote);
159 if (pFootnoteFormat)
160 {
161 pFootnoteFormat->SetXFootnote(xNote);
162 }
163 // need a permanent Reference to initialize m_wThis
164 pNote->m_pImpl->m_wThis = xNote;
165 }
166 return xNote;
167 }
168
getUnoTunnelId()169 const uno::Sequence< sal_Int8 > & SwXFootnote::getUnoTunnelId()
170 {
171 static const UnoTunnelIdInit theSwXFootnoteUnoTunnelId;
172 return theSwXFootnoteUnoTunnelId.getSeq();
173 }
174
175 sal_Int64 SAL_CALL
getSomething(const uno::Sequence<sal_Int8> & rId)176 SwXFootnote::getSomething(const uno::Sequence< sal_Int8 >& rId)
177 {
178 const sal_Int64 nRet( ::sw::UnoTunnelImpl<SwXFootnote>(rId, this) );
179 return nRet ? nRet : SwXText::getSomething(rId);
180 }
181
182 OUString SAL_CALL
getImplementationName()183 SwXFootnote::getImplementationName()
184 {
185 return "SwXFootnote";
186 }
187
188 char const*const g_ServicesFootnote[] =
189 {
190 "com.sun.star.text.TextContent",
191 "com.sun.star.text.Footnote",
192 "com.sun.star.text.Text",
193 "com.sun.star.text.Endnote", // NB: only supported for endnotes!
194 };
195
196 const size_t g_nServicesEndnote( SAL_N_ELEMENTS(g_ServicesFootnote) );
197
198 const size_t g_nServicesFootnote( g_nServicesEndnote - 1 ); // NB: omit!
199
supportsService(const OUString & rServiceName)200 sal_Bool SAL_CALL SwXFootnote::supportsService(const OUString& rServiceName)
201 {
202 return cppu::supportsService(this, rServiceName);
203 }
204
205 uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames()206 SwXFootnote::getSupportedServiceNames()
207 {
208 SolarMutexGuard g;
209 return GetSupportedServiceNamesImpl(
210 (m_pImpl->m_bIsEndnote) ? g_nServicesEndnote : g_nServicesFootnote,
211 g_ServicesFootnote);
212 }
213
214 uno::Sequence< uno::Type > SAL_CALL
getTypes()215 SwXFootnote::getTypes()
216 {
217 const uno::Sequence< uno::Type > aTypes = SwXFootnote_Base::getTypes();
218 const uno::Sequence< uno::Type > aTextTypes = SwXText::getTypes();
219 return ::comphelper::concatSequences(aTypes, aTextTypes);
220 }
221
222 uno::Sequence< sal_Int8 > SAL_CALL
getImplementationId()223 SwXFootnote::getImplementationId()
224 {
225 return css::uno::Sequence<sal_Int8>();
226 }
227
228 uno::Any SAL_CALL
queryInterface(const uno::Type & rType)229 SwXFootnote::queryInterface(const uno::Type& rType)
230 {
231 const uno::Any ret = SwXFootnote_Base::queryInterface(rType);
232 return (ret.getValueType() == cppu::UnoType<void>::get())
233 ? SwXText::queryInterface(rType)
234 : ret;
235 }
236
getLabel()237 OUString SAL_CALL SwXFootnote::getLabel()
238 {
239 SolarMutexGuard aGuard;
240
241 OUString sRet;
242 SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
243 if(pFormat)
244 {
245 sRet = pFormat->GetNumStr();
246 }
247 else if (m_pImpl->m_bIsDescriptor)
248 {
249 sRet = m_pImpl->m_sLabel;
250 }
251 else
252 {
253 throw uno::RuntimeException();
254 }
255 return sRet;
256 }
257
258 void SAL_CALL
setLabel(const OUString & aLabel)259 SwXFootnote::setLabel(const OUString& aLabel)
260 {
261 SolarMutexGuard aGuard;
262 OUString newLabel(aLabel);
263 //new line must not occur as footnote label
264 if(newLabel.indexOf('\n') >=0 )
265 {
266 newLabel = newLabel.replace('\n', ' ');
267 }
268 SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
269 if(pFormat)
270 {
271 const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote();
272 OSL_ENSURE(pTextFootnote, "No TextNode?");
273 SwTextNode& rTextNode = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
274
275 SwPaM aPam(rTextNode, pTextFootnote->GetStart());
276 GetDoc()->SetCurFootnote(aPam, newLabel, pFormat->IsEndNote());
277 }
278 else if (m_pImpl->m_bIsDescriptor)
279 {
280 m_pImpl->m_sLabel = newLabel;
281 }
282 else
283 {
284 throw uno::RuntimeException();
285 }
286 }
287
288 void SAL_CALL
attach(const uno::Reference<text::XTextRange> & xTextRange)289 SwXFootnote::attach(const uno::Reference< text::XTextRange > & xTextRange)
290 {
291 SolarMutexGuard aGuard;
292
293 if (!m_pImpl->m_bIsDescriptor)
294 {
295 throw uno::RuntimeException();
296 }
297 const uno::Reference<lang::XUnoTunnel> xRangeTunnel(
298 xTextRange, uno::UNO_QUERY);
299 SwXTextRange *const pRange =
300 ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel);
301 OTextCursorHelper *const pCursor =
302 ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel);
303 SwDoc *const pNewDoc =
304 pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
305 if (!pNewDoc)
306 {
307 throw lang::IllegalArgumentException();
308 }
309
310 SwUnoInternalPaM aPam(*pNewDoc);
311 // this now needs to return TRUE
312 ::sw::XTextRangeToSwPaM(aPam, xTextRange);
313
314 UnoActionContext aCont(pNewDoc);
315 pNewDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
316 aPam.DeleteMark();
317 SwFormatFootnote aFootNote(m_pImpl->m_bIsEndnote);
318 if (!m_pImpl->m_sLabel.isEmpty())
319 {
320 aFootNote.SetNumStr(m_pImpl->m_sLabel);
321 }
322
323 SwXTextCursor const*const pTextCursor(
324 dynamic_cast<SwXTextCursor*>(pCursor));
325 const bool bForceExpandHints( pTextCursor && pTextCursor->IsAtEndOfMeta() );
326 const SetAttrMode nInsertFlags = bForceExpandHints
327 ? SetAttrMode::FORCEHINTEXPAND
328 : SetAttrMode::DEFAULT;
329
330 pNewDoc->getIDocumentContentOperations().InsertPoolItem(aPam, aFootNote, nInsertFlags);
331
332 SwTextFootnote *const pTextAttr = static_cast<SwTextFootnote*>(
333 aPam.GetNode().GetTextNode()->GetTextAttrForCharAt(
334 aPam.GetPoint()->nContent.GetIndex()-1, RES_TXTATR_FTN ));
335
336 if (pTextAttr)
337 {
338 m_pImpl->EndListeningAll();
339 SwFormatFootnote* pFootnote = const_cast<SwFormatFootnote*>(&pTextAttr->GetFootnote());
340 m_pImpl->m_pFormatFootnote = pFootnote;
341 m_pImpl->StartListening(pFootnote->GetNotifier());
342 // force creation of sequence id - is used for references
343 if (pNewDoc->IsInReading())
344 {
345 pTextAttr->SetSeqNo(pNewDoc->GetFootnoteIdxs().size());
346 }
347 else
348 {
349 pTextAttr->SetSeqRefNo();
350 }
351 }
352 m_pImpl->m_bIsDescriptor = false;
353 SetDoc(pNewDoc);
354 }
355
356 uno::Reference< text::XTextRange > SAL_CALL
getAnchor()357 SwXFootnote::getAnchor()
358 {
359 SolarMutexGuard aGuard;
360 return m_pImpl->GetFootnoteFormatOrThrow().getAnchor(*GetDoc());
361 }
362
dispose()363 void SAL_CALL SwXFootnote::dispose()
364 {
365 SolarMutexGuard aGuard;
366
367 SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
368
369 SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
370 OSL_ENSURE(pTextFootnote, "no TextNode?");
371 SwTextNode& rTextNode = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
372 const sal_Int32 nPos = pTextFootnote->GetStart();
373 SwPaM aPam(rTextNode, nPos, rTextNode, nPos+1);
374 GetDoc()->getIDocumentContentOperations().DeleteAndJoin( aPam );
375 }
376
377 void SAL_CALL
addEventListener(const uno::Reference<lang::XEventListener> & xListener)378 SwXFootnote::addEventListener(
379 const uno::Reference< lang::XEventListener > & xListener)
380 {
381 // no need to lock here as m_pImpl is const and container threadsafe
382 m_pImpl->m_EventListeners.addInterface(xListener);
383 }
384
385 void SAL_CALL
removeEventListener(const uno::Reference<lang::XEventListener> & xListener)386 SwXFootnote::removeEventListener(
387 const uno::Reference< lang::XEventListener > & xListener)
388 {
389 // no need to lock here as m_pImpl is const and container threadsafe
390 m_pImpl->m_EventListeners.removeInterface(xListener);
391 }
392
GetStartNode() const393 const SwStartNode *SwXFootnote::GetStartNode() const
394 {
395 SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
396 if(pFormat)
397 {
398 const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote();
399 if( pTextFootnote )
400 {
401 return pTextFootnote->GetStartNode()->GetNode().GetStartNode();
402 }
403 }
404 return nullptr;
405 }
406
407 uno::Reference< text::XTextCursor >
CreateCursor()408 SwXFootnote::CreateCursor()
409 {
410 return createTextCursor();
411 }
412
413 uno::Reference< text::XTextCursor > SAL_CALL
createTextCursor()414 SwXFootnote::createTextCursor()
415 {
416 SolarMutexGuard aGuard;
417
418 SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
419
420 SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
421 SwPosition aPos( *pTextFootnote->GetStartNode() );
422 rtl::Reference<SwXTextCursor> pXCursor =
423 new SwXTextCursor(*GetDoc(), this, CursorType::Footnote, aPos);
424 auto& rUnoCursor(pXCursor->GetCursor());
425 rUnoCursor.Move(fnMoveForward, GoInNode);
426 return static_cast<text::XWordCursor*>(pXCursor.get());
427 }
428
429 uno::Reference< text::XTextCursor > SAL_CALL
createTextCursorByRange(const uno::Reference<text::XTextRange> & xTextPosition)430 SwXFootnote::createTextCursorByRange(
431 const uno::Reference< text::XTextRange > & xTextPosition)
432 {
433 SolarMutexGuard aGuard;
434
435 SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
436
437 SwUnoInternalPaM aPam(*GetDoc());
438 if (!::sw::XTextRangeToSwPaM(aPam, xTextPosition))
439 {
440 throw uno::RuntimeException();
441 }
442
443 SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
444 SwNode const*const pFootnoteStartNode = &pTextFootnote->GetStartNode()->GetNode();
445
446 const SwNode* pStart = aPam.GetNode().FindFootnoteStartNode();
447 if (pStart != pFootnoteStartNode)
448 {
449 throw uno::RuntimeException();
450 }
451
452 const uno::Reference< text::XTextCursor > xRet =
453 static_cast<text::XWordCursor*>(
454 new SwXTextCursor(*GetDoc(), this, CursorType::Footnote,
455 *aPam.GetPoint(), aPam.GetMark()));
456 return xRet;
457 }
458
459 uno::Reference< container::XEnumeration > SAL_CALL
createEnumeration()460 SwXFootnote::createEnumeration()
461 {
462 SolarMutexGuard aGuard;
463
464 SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
465
466 SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
467 SwPosition aPos( *pTextFootnote->GetStartNode() );
468 auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
469 pUnoCursor->Move(fnMoveForward, GoInNode);
470 return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Footnote);
471 }
472
getElementType()473 uno::Type SAL_CALL SwXFootnote::getElementType()
474 {
475 return cppu::UnoType<text::XTextRange>::get();
476 }
477
hasElements()478 sal_Bool SAL_CALL SwXFootnote::hasElements()
479 {
480 return true;
481 }
482
483 uno::Reference< beans::XPropertySetInfo > SAL_CALL
getPropertySetInfo()484 SwXFootnote::getPropertySetInfo()
485 {
486 SolarMutexGuard g;
487 static uno::Reference< beans::XPropertySetInfo > xRet =
488 aSwMapProvider.GetPropertySet(PROPERTY_MAP_FOOTNOTE)
489 ->getPropertySetInfo();
490 return xRet;
491 }
492
493 void SAL_CALL
setPropertyValue(const OUString &,const uno::Any &)494 SwXFootnote::setPropertyValue(const OUString&, const uno::Any&)
495 {
496 //no values to be set
497 throw lang::IllegalArgumentException();
498 }
499
500 uno::Any SAL_CALL
getPropertyValue(const OUString & rPropertyName)501 SwXFootnote::getPropertyValue(const OUString& rPropertyName)
502 {
503 SolarMutexGuard aGuard;
504
505 uno::Any aRet;
506 if (! ::sw::GetDefaultTextContentValue(aRet, rPropertyName))
507 {
508 if (rPropertyName == UNO_NAME_START_REDLINE ||
509 rPropertyName == UNO_NAME_END_REDLINE)
510 {
511 //redline can only be returned if it's a living object
512 if (!m_pImpl->m_bIsDescriptor)
513 {
514 aRet = SwXText::getPropertyValue(rPropertyName);
515 }
516 }
517 else if (rPropertyName == UNO_NAME_REFERENCE_ID)
518 {
519 SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
520 if (pFormat)
521 {
522 SwTextFootnote const*const pTextFootnote = pFormat->GetTextFootnote();
523 OSL_ENSURE(pTextFootnote, "no TextNode?");
524 aRet <<= static_cast<sal_Int16>(pTextFootnote->GetSeqRefNo());
525 }
526 }
527 else
528 {
529 beans::UnknownPropertyException aExcept;
530 aExcept.Message = rPropertyName;
531 throw aExcept;
532 }
533 }
534 return aRet;
535 }
536
537 void SAL_CALL
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)538 SwXFootnote::addPropertyChangeListener(
539 const OUString& /*rPropertyName*/,
540 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
541 {
542 OSL_FAIL("SwXFootnote::addPropertyChangeListener(): not implemented");
543 }
544
545 void SAL_CALL
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)546 SwXFootnote::removePropertyChangeListener(
547 const OUString& /*rPropertyName*/,
548 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
549 {
550 OSL_FAIL("SwXFootnote::removePropertyChangeListener(): not implemented");
551 }
552
553 void SAL_CALL
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)554 SwXFootnote::addVetoableChangeListener(
555 const OUString& /*rPropertyName*/,
556 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
557 {
558 OSL_FAIL("SwXFootnote::addVetoableChangeListener(): not implemented");
559 }
560
561 void SAL_CALL
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)562 SwXFootnote::removeVetoableChangeListener(
563 const OUString& /*rPropertyName*/,
564 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
565 {
566 OSL_FAIL("SwXFootnote::removeVetoableChangeListener(): not implemented");
567 }
568
569 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
570