resolves tdf#164985 Incorrect bookmarks list in bookmark dialog after
[LibreOffice.git] / sw / source / core / unocore / unoftn.cxx
blobbbfa61e62a6379c97b3d2512669f11449a06c8ba
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
27 #include <mutex>
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>
43 using namespace ::com::sun::star;
45 namespace {
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); });
56 return ret;
61 class SwXFootnote::Impl
62 : public SvtListener
64 public:
66 SwXFootnote& m_rThis;
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;
71 bool m_bIsDescriptor;
72 SwFormatFootnote* m_pFormatFootnote;
73 OUString m_sLabel;
75 Impl(SwXFootnote& rThis,
76 SwFormatFootnote* const pFootnote,
77 const bool bIsEndnote)
78 : m_rThis(rThis)
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() );
93 if (!pFootnote) {
94 throw uno::RuntimeException(u"SwXFootnote: disposed or invalid"_ustr, nullptr);
96 return *pFootnote;
99 void Invalidate();
100 protected:
101 void Notify(const SfxHint& rHint) override;
104 void SwXFootnote::Impl::Invalidate()
106 EndListeningAll();
107 m_pFormatFootnote = nullptr;
108 m_rThis.SetDoc(nullptr);
109 uno::Reference<uno::XInterface> const xThis(m_wThis);
110 if (!xThis.is())
111 { // fdo#72695: if UNO object is already dead, don't revive it with event
112 return;
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)
122 Invalidate();
125 void SwXFootnote::OnFormatFootnoteDeleted()
127 Invalidate();
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;
152 if (pFootnoteFormat)
154 xNote = pFootnoteFormat->GetXFootnote();
156 if (!xNote.is())
158 if (pFootnoteFormat)
160 xNote = new SwXFootnote(rDoc, *pFootnoteFormat);
161 pFootnoteFormat->SetXFootnote(xNote);
163 else
164 xNote = new SwXFootnote(isEndnote);
166 // need a permanent Reference to initialize m_wThis
167 xNote->m_pImpl->m_wThis = xNote.get();
169 return xNote;
172 OUString SAL_CALL
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()
198 SolarMutexGuard g;
199 return GetSupportedServiceNamesImpl(
200 (m_pImpl->m_bIsEndnote) ? g_nServicesEndnote : g_nServicesFootnote,
201 g_ServicesFootnote);
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>();
218 uno::Any SAL_CALL
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)
224 : ret;
227 OUString SAL_CALL SwXFootnote::getLabel()
229 SolarMutexGuard aGuard;
231 OUString sRet;
232 SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
233 if(pFormat)
235 sRet = pFormat->GetNumStr();
237 else if (m_pImpl->m_bIsDescriptor)
239 sRet = m_pImpl->m_sLabel;
241 else
243 throw uno::RuntimeException();
245 return sRet;
248 void SAL_CALL
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();
259 if(pFormat)
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;
272 else
274 throw uno::RuntimeException();
278 void SAL_CALL
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);
291 if (!pNewDoc)
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);
302 aPam.DeleteMark();
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 ));
322 if (pTextAttr)
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());
333 else
335 pTextAttr->SetSeqRefNo();
338 m_pImpl->m_bIsDescriptor = false;
339 SetDoc(pNewDoc);
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 );
363 void SAL_CALL
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);
372 void SAL_CALL
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();
384 if(pFormat)
386 const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote();
387 if( pTextFootnote )
389 return pTextFootnote->GetStartNode()->GetNode().GetStartNode();
392 return nullptr;
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);
406 return pXCursor;
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());
438 return xRet;
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()
462 return true;
465 uno::Reference< beans::XPropertySetInfo > SAL_CALL
466 SwXFootnote::getPropertySetInfo()
468 SolarMutexGuard g;
469 static uno::Reference< beans::XPropertySetInfo > xRet =
470 aSwMapProvider.GetPropertySet(PROPERTY_MAP_FOOTNOTE)
471 ->getPropertySetInfo();
472 return xRet;
475 void SAL_CALL
476 SwXFootnote::setPropertyValue(const OUString&, const uno::Any&)
478 //no values to be set
479 throw lang::IllegalArgumentException();
482 uno::Any SAL_CALL
483 SwXFootnote::getPropertyValue(const OUString& rPropertyName)
485 SolarMutexGuard aGuard;
487 uno::Any aRet;
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();
502 if (pFormat)
504 SwTextFootnote const*const pTextFootnote = pFormat->GetTextFootnote();
505 assert(pTextFootnote && "no TextNode?");
506 aRet <<= static_cast<sal_Int16>(pTextFootnote->GetSeqRefNo());
509 else
511 throw beans::UnknownPropertyException(rPropertyName);
514 return aRet;
517 void SAL_CALL
518 SwXFootnote::addPropertyChangeListener(
519 const OUString& /*rPropertyName*/,
520 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
522 OSL_FAIL("SwXFootnote::addPropertyChangeListener(): not implemented");
525 void SAL_CALL
526 SwXFootnote::removePropertyChangeListener(
527 const OUString& /*rPropertyName*/,
528 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
530 OSL_FAIL("SwXFootnote::removePropertyChangeListener(): not implemented");
533 void SAL_CALL
534 SwXFootnote::addVetoableChangeListener(
535 const OUString& /*rPropertyName*/,
536 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
538 OSL_FAIL("SwXFootnote::addVetoableChangeListener(): not implemented");
541 void SAL_CALL
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: */