1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <comphelper/interfacecontainer4.hxx>
23 #include <comphelper/sequence.hxx>
24 #include <comphelper/servicehelper.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <svl/listener.hxx>
29 #include <unofootnote.hxx>
30 #include <unotextrange.hxx>
31 #include <unotextcursor.hxx>
32 #include <unoparagraph.hxx>
34 #include <unoprnms.hxx>
40 #include <unocrsr.hxx>
41 #include <svl/itemprop.hxx>
43 using namespace ::com::sun::star
;
47 uno::Sequence
< OUString
>
48 GetSupportedServiceNamesImpl(
49 size_t const nServices
, char const*const pServices
[])
51 uno::Sequence
< OUString
> ret(static_cast<sal_Int32
>(nServices
));
53 std::transform(pServices
, pServices
+ nServices
, ret
.getArray(),
54 [](const char* pService
) -> OUString
{ return OUString::createFromAscii(pService
); });
61 class SwXFootnote::Impl
67 unotools::WeakReference
<SwXFootnote
> m_wThis
;
68 const bool m_bIsEndnote
;
69 std::mutex m_Mutex
; // just for OInterfaceContainerHelper4
70 ::comphelper::OInterfaceContainerHelper4
<css::lang::XEventListener
> m_EventListeners
;
72 SwFormatFootnote
* m_pFormatFootnote
;
75 Impl(SwXFootnote
& rThis
,
76 SwFormatFootnote
* const pFootnote
,
77 const bool bIsEndnote
)
79 , m_bIsEndnote(bIsEndnote
)
80 , m_bIsDescriptor(nullptr == pFootnote
)
81 , m_pFormatFootnote(pFootnote
)
83 if (m_pFormatFootnote
)
84 StartListening(*m_pFormatFootnote
);
87 const SwFormatFootnote
* GetFootnoteFormat() const {
88 return m_rThis
.GetDoc() ? m_pFormatFootnote
: nullptr;
91 SwFormatFootnote
const& GetFootnoteFormatOrThrow() const {
92 SwFormatFootnote
const*const pFootnote( GetFootnoteFormat() );
94 throw uno::RuntimeException(u
"SwXFootnote: disposed or invalid"_ustr
, nullptr);
101 void Notify(const SfxHint
& rHint
) override
;
104 void SwXFootnote::Impl::Invalidate()
107 m_pFormatFootnote
= nullptr;
108 m_rThis
.SetDoc(nullptr);
109 uno::Reference
<uno::XInterface
> const xThis(m_wThis
);
111 { // fdo#72695: if UNO object is already dead, don't revive it with event
114 lang::EventObject
const ev(xThis
);
115 std::unique_lock
aGuard(m_Mutex
);
116 m_EventListeners
.disposeAndClear(aGuard
, ev
);
119 void SwXFootnote::Impl::Notify(const SfxHint
& rHint
)
121 if(rHint
.GetId() == SfxHintId::Dying
)
125 void SwXFootnote::OnFormatFootnoteDeleted()
130 SwXFootnote::SwXFootnote(const bool bEndnote
)
131 : SwXText(nullptr, CursorType::Footnote
)
132 , m_pImpl( new SwXFootnote::Impl(*this, nullptr, bEndnote
) )
136 SwXFootnote::SwXFootnote(SwDoc
& rDoc
, SwFormatFootnote
& rFormat
)
137 : SwXText(& rDoc
, CursorType::Footnote
)
138 , m_pImpl( new SwXFootnote::Impl(*this, &rFormat
, rFormat
.IsEndNote()) )
142 SwXFootnote::~SwXFootnote()
146 rtl::Reference
<SwXFootnote
>
147 SwXFootnote::CreateXFootnote(SwDoc
& rDoc
, SwFormatFootnote
*const pFootnoteFormat
,
148 bool const isEndnote
)
150 // i#105557: do not iterate over the registered clients: race condition
151 rtl::Reference
<SwXFootnote
> xNote
;
154 xNote
= pFootnoteFormat
->GetXFootnote();
160 xNote
= new SwXFootnote(rDoc
, *pFootnoteFormat
);
161 pFootnoteFormat
->SetXFootnote(xNote
);
164 xNote
= new SwXFootnote(isEndnote
);
166 // need a permanent Reference to initialize m_wThis
167 xNote
->m_pImpl
->m_wThis
= xNote
.get();
173 SwXFootnote::getImplementationName()
175 return u
"SwXFootnote"_ustr
;
178 char const*const g_ServicesFootnote
[] =
180 "com.sun.star.text.TextContent",
181 "com.sun.star.text.Footnote",
182 "com.sun.star.text.Text",
183 "com.sun.star.text.Endnote", // NB: only supported for endnotes!
186 const size_t g_nServicesEndnote( SAL_N_ELEMENTS(g_ServicesFootnote
) );
188 const size_t g_nServicesFootnote( g_nServicesEndnote
- 1 ); // NB: omit!
190 sal_Bool SAL_CALL
SwXFootnote::supportsService(const OUString
& rServiceName
)
192 return cppu::supportsService(this, rServiceName
);
195 uno::Sequence
< OUString
> SAL_CALL
196 SwXFootnote::getSupportedServiceNames()
199 return GetSupportedServiceNamesImpl(
200 (m_pImpl
->m_bIsEndnote
) ? g_nServicesEndnote
: g_nServicesFootnote
,
204 uno::Sequence
< uno::Type
> SAL_CALL
205 SwXFootnote::getTypes()
207 const uno::Sequence
< uno::Type
> aTypes
= SwXFootnote_Base::getTypes();
208 const uno::Sequence
< uno::Type
> aTextTypes
= SwXText::getTypes();
209 return ::comphelper::concatSequences(aTypes
, aTextTypes
);
212 uno::Sequence
< sal_Int8
> SAL_CALL
213 SwXFootnote::getImplementationId()
215 return css::uno::Sequence
<sal_Int8
>();
219 SwXFootnote::queryInterface(const uno::Type
& rType
)
221 const uno::Any ret
= SwXFootnote_Base::queryInterface(rType
);
222 return (ret
.getValueType() == cppu::UnoType
<void>::get())
223 ? SwXText::queryInterface(rType
)
227 OUString SAL_CALL
SwXFootnote::getLabel()
229 SolarMutexGuard aGuard
;
232 SwFormatFootnote
const*const pFormat
= m_pImpl
->GetFootnoteFormat();
235 sRet
= pFormat
->GetNumStr();
237 else if (m_pImpl
->m_bIsDescriptor
)
239 sRet
= m_pImpl
->m_sLabel
;
243 throw uno::RuntimeException();
249 SwXFootnote::setLabel(const OUString
& aLabel
)
251 SolarMutexGuard aGuard
;
252 OUString
newLabel(aLabel
);
253 //new line must not occur as footnote label
254 if(newLabel
.indexOf('\n') >=0 )
256 newLabel
= newLabel
.replace('\n', ' ');
258 SwFormatFootnote
const*const pFormat
= m_pImpl
->GetFootnoteFormat();
261 const SwTextFootnote
* pTextFootnote
= pFormat
->GetTextFootnote();
262 assert(pTextFootnote
&& "No TextNode?");
263 SwTextNode
& rTextNode
= const_cast<SwTextNode
&>(pTextFootnote
->GetTextNode());
265 SwPaM
aPam(rTextNode
, pTextFootnote
->GetStart());
266 GetDoc()->SetCurFootnote(aPam
, newLabel
, pFormat
->IsEndNote());
268 else if (m_pImpl
->m_bIsDescriptor
)
270 m_pImpl
->m_sLabel
= newLabel
;
274 throw uno::RuntimeException();
279 SwXFootnote::attach(const uno::Reference
< text::XTextRange
> & xTextRange
)
281 SolarMutexGuard aGuard
;
283 if (!m_pImpl
->m_bIsDescriptor
)
285 throw uno::RuntimeException();
287 SwXTextRange
*const pRange
= dynamic_cast<SwXTextRange
*>(xTextRange
.get());
288 OTextCursorHelper
*const pCursor
= dynamic_cast<OTextCursorHelper
*>(xTextRange
.get());
289 SwDoc
*const pNewDoc
=
290 pRange
? &pRange
->GetDoc() : (pCursor
? pCursor
->GetDoc() : nullptr);
293 throw lang::IllegalArgumentException();
296 SwUnoInternalPaM
aPam(*pNewDoc
);
297 // this now needs to return TRUE
298 ::sw::XTextRangeToSwPaM(aPam
, xTextRange
);
300 UnoActionContext
aCont(pNewDoc
);
301 pNewDoc
->getIDocumentContentOperations().DeleteAndJoin(aPam
);
303 SwFormatFootnote
aFootNote(m_pImpl
->m_bIsEndnote
);
304 if (!m_pImpl
->m_sLabel
.isEmpty())
306 aFootNote
.SetNumStr(m_pImpl
->m_sLabel
);
309 SwXTextCursor
const*const pTextCursor(
310 dynamic_cast<SwXTextCursor
*>(pCursor
));
311 const bool bForceExpandHints( pTextCursor
&& pTextCursor
->IsAtEndOfMeta() );
312 const SetAttrMode nInsertFlags
= bForceExpandHints
313 ? SetAttrMode::FORCEHINTEXPAND
314 : SetAttrMode::DEFAULT
;
316 pNewDoc
->getIDocumentContentOperations().InsertPoolItem(aPam
, aFootNote
, nInsertFlags
);
318 SwTextFootnote
*const pTextAttr
= static_cast<SwTextFootnote
*>(
319 aPam
.GetPointNode().GetTextNode()->GetTextAttrForCharAt(
320 aPam
.GetPoint()->GetContentIndex()-1, RES_TXTATR_FTN
));
324 m_pImpl
->EndListeningAll();
325 SwFormatFootnote
* pFootnote
= const_cast<SwFormatFootnote
*>(&pTextAttr
->GetFootnote());
326 m_pImpl
->m_pFormatFootnote
= pFootnote
;
327 m_pImpl
->StartListening(*pFootnote
);
328 // force creation of sequence id - is used for references
329 if (pNewDoc
->IsInReading())
331 pTextAttr
->SetSeqNo(pNewDoc
->GetFootnoteIdxs().size());
335 pTextAttr
->SetSeqRefNo();
338 m_pImpl
->m_bIsDescriptor
= false;
342 uno::Reference
< text::XTextRange
> SAL_CALL
343 SwXFootnote::getAnchor()
345 SolarMutexGuard aGuard
;
346 return m_pImpl
->GetFootnoteFormatOrThrow().getAnchor(*GetDoc());
349 void SAL_CALL
SwXFootnote::dispose()
351 SolarMutexGuard aGuard
;
353 SwFormatFootnote
const& rFormat( m_pImpl
->GetFootnoteFormatOrThrow() );
355 SwTextFootnote
const*const pTextFootnote
= rFormat
.GetTextFootnote();
356 assert(pTextFootnote
&& "no TextNode?");
357 SwTextNode
& rTextNode
= const_cast<SwTextNode
&>(pTextFootnote
->GetTextNode());
358 const sal_Int32 nPos
= pTextFootnote
->GetStart();
359 SwPaM
aPam(rTextNode
, nPos
, rTextNode
, nPos
+1);
360 GetDoc()->getIDocumentContentOperations().DeleteAndJoin( aPam
);
364 SwXFootnote::addEventListener(
365 const uno::Reference
< lang::XEventListener
> & xListener
)
367 // no need to lock here as m_pImpl is const and container threadsafe
368 std::unique_lock
aGuard(m_pImpl
->m_Mutex
);
369 m_pImpl
->m_EventListeners
.addInterface(aGuard
, xListener
);
373 SwXFootnote::removeEventListener(
374 const uno::Reference
< lang::XEventListener
> & xListener
)
376 // no need to lock here as m_pImpl is const and container threadsafe
377 std::unique_lock
aGuard(m_pImpl
->m_Mutex
);
378 m_pImpl
->m_EventListeners
.removeInterface(aGuard
, xListener
);
381 const SwStartNode
*SwXFootnote::GetStartNode() const
383 SwFormatFootnote
const*const pFormat
= m_pImpl
->GetFootnoteFormat();
386 const SwTextFootnote
* pTextFootnote
= pFormat
->GetTextFootnote();
389 return pTextFootnote
->GetStartNode()->GetNode().GetStartNode();
395 rtl::Reference
< SwXTextCursor
>
396 SwXFootnote::createXTextCursor()
398 SwFormatFootnote
const& rFormat( m_pImpl
->GetFootnoteFormatOrThrow() );
400 SwTextFootnote
const*const pTextFootnote
= rFormat
.GetTextFootnote();
401 SwPosition
aPos( *pTextFootnote
->GetStartNode() );
402 rtl::Reference
<SwXTextCursor
> pXCursor
=
403 new SwXTextCursor(*GetDoc(), this, CursorType::Footnote
, aPos
);
404 auto& rUnoCursor(pXCursor
->GetCursor());
405 rUnoCursor
.Move(fnMoveForward
, GoInNode
);
409 rtl::Reference
< SwXTextCursor
>
410 SwXFootnote::createXTextCursorByRange(
411 const uno::Reference
< text::XTextRange
> & xTextPosition
)
413 SwUnoInternalPaM
aPam(*GetDoc());
414 if (!::sw::XTextRangeToSwPaM(aPam
, xTextPosition
))
416 throw uno::RuntimeException();
418 return createXTextCursorByRangeImpl(aPam
);
421 rtl::Reference
< SwXTextCursor
> SwXFootnote::createXTextCursorByRangeImpl(
422 SwUnoInternalPaM
& rPam
)
424 SwFormatFootnote
const& rFormat( m_pImpl
->GetFootnoteFormatOrThrow() );
426 SwTextFootnote
const*const pTextFootnote
= rFormat
.GetTextFootnote();
427 SwNode
const*const pFootnoteStartNode
= &pTextFootnote
->GetStartNode()->GetNode();
429 const SwNode
* pStart
= rPam
.GetPointNode().FindFootnoteStartNode();
430 if (pStart
!= pFootnoteStartNode
)
432 throw uno::RuntimeException();
435 const rtl::Reference
< SwXTextCursor
> xRet
=
436 new SwXTextCursor(*GetDoc(), this, CursorType::Footnote
,
437 *rPam
.GetPoint(), rPam
.GetMark());
441 uno::Reference
< container::XEnumeration
> SAL_CALL
442 SwXFootnote::createEnumeration()
444 SolarMutexGuard aGuard
;
446 SwFormatFootnote
const& rFormat( m_pImpl
->GetFootnoteFormatOrThrow() );
448 SwTextFootnote
const*const pTextFootnote
= rFormat
.GetTextFootnote();
449 SwPosition
aPos( *pTextFootnote
->GetStartNode() );
450 auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos
));
451 pUnoCursor
->Move(fnMoveForward
, GoInNode
);
452 return SwXParagraphEnumeration::Create(this, pUnoCursor
, CursorType::Footnote
);
455 uno::Type SAL_CALL
SwXFootnote::getElementType()
457 return cppu::UnoType
<text::XTextRange
>::get();
460 sal_Bool SAL_CALL
SwXFootnote::hasElements()
465 uno::Reference
< beans::XPropertySetInfo
> SAL_CALL
466 SwXFootnote::getPropertySetInfo()
469 static uno::Reference
< beans::XPropertySetInfo
> xRet
=
470 aSwMapProvider
.GetPropertySet(PROPERTY_MAP_FOOTNOTE
)
471 ->getPropertySetInfo();
476 SwXFootnote::setPropertyValue(const OUString
&, const uno::Any
&)
478 //no values to be set
479 throw lang::IllegalArgumentException();
483 SwXFootnote::getPropertyValue(const OUString
& rPropertyName
)
485 SolarMutexGuard aGuard
;
488 if (! ::sw::GetDefaultTextContentValue(aRet
, rPropertyName
))
490 if (rPropertyName
== UNO_NAME_START_REDLINE
||
491 rPropertyName
== UNO_NAME_END_REDLINE
)
493 //redline can only be returned if it's a living object
494 if (!m_pImpl
->m_bIsDescriptor
)
496 aRet
= SwXText::getPropertyValue(rPropertyName
);
499 else if (rPropertyName
== UNO_NAME_REFERENCE_ID
)
501 SwFormatFootnote
const*const pFormat
= m_pImpl
->GetFootnoteFormat();
504 SwTextFootnote
const*const pTextFootnote
= pFormat
->GetTextFootnote();
505 assert(pTextFootnote
&& "no TextNode?");
506 aRet
<<= static_cast<sal_Int16
>(pTextFootnote
->GetSeqRefNo());
511 throw beans::UnknownPropertyException(rPropertyName
);
518 SwXFootnote::addPropertyChangeListener(
519 const OUString
& /*rPropertyName*/,
520 const uno::Reference
< beans::XPropertyChangeListener
>& /*xListener*/)
522 OSL_FAIL("SwXFootnote::addPropertyChangeListener(): not implemented");
526 SwXFootnote::removePropertyChangeListener(
527 const OUString
& /*rPropertyName*/,
528 const uno::Reference
< beans::XPropertyChangeListener
>& /*xListener*/)
530 OSL_FAIL("SwXFootnote::removePropertyChangeListener(): not implemented");
534 SwXFootnote::addVetoableChangeListener(
535 const OUString
& /*rPropertyName*/,
536 const uno::Reference
< beans::XVetoableChangeListener
>& /*xListener*/)
538 OSL_FAIL("SwXFootnote::addVetoableChangeListener(): not implemented");
542 SwXFootnote::removeVetoableChangeListener(
543 const OUString
& /*rPropertyName*/,
544 const uno::Reference
< beans::XVetoableChangeListener
>& /*xListener*/)
546 OSL_FAIL("SwXFootnote::removeVetoableChangeListener(): not implemented");
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */