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