Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / unocore / unotextmarkup.cxx
blob4bbc8601608d988d9490b6bdf3eb94b07ad22813
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 <unotextmarkup.hxx>
22 #include <comphelper/servicehelper.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <osl/diagnose.h>
25 #include <svl/listener.hxx>
26 #include <utility>
27 #include <vcl/svapp.hxx>
28 #include <SwSmartTagMgr.hxx>
29 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
30 #include <com/sun/star/text/TextMarkupType.hpp>
31 #include <com/sun/star/text/TextMarkupDescriptor.hpp>
32 #include <com/sun/star/container/ElementExistException.hpp>
33 #include <com/sun/star/container/XStringKeyMap.hpp>
34 #include <ndtxt.hxx>
35 #include <SwGrammarMarkUp.hxx>
36 #include <TextCursorHelper.hxx>
37 #include <GrammarContact.hxx>
39 #include <com/sun/star/lang/XUnoTunnel.hpp>
40 #include <com/sun/star/text/XTextRange.hpp>
42 #include <pam.hxx>
44 #include <unotextrange.hxx>
45 #include <modeltoviewhelper.hxx>
47 using namespace ::com::sun::star;
49 struct SwXTextMarkup::Impl
50 : public SvtListener
52 SwTextNode* m_pTextNode;
53 ModelToViewHelper const m_ConversionMap;
55 Impl(SwTextNode* const pTextNode, ModelToViewHelper aMap)
56 : m_pTextNode(pTextNode)
57 , m_ConversionMap(std::move(aMap))
59 if(m_pTextNode)
60 StartListening(pTextNode->GetNotifier());
63 virtual void Notify(const SfxHint& rHint) override;
66 SwXTextMarkup::SwXTextMarkup(
67 SwTextNode *const pTextNode, const ModelToViewHelper& rMap)
68 : m_pImpl(new Impl(pTextNode, rMap))
72 SwXTextMarkup::~SwXTextMarkup()
76 SwTextNode* SwXTextMarkup::GetTextNode()
78 return m_pImpl->m_pTextNode;
81 void SwXTextMarkup::ClearTextNode()
83 m_pImpl->m_pTextNode = nullptr;
84 m_pImpl->EndListeningAll();
87 const ModelToViewHelper& SwXTextMarkup::GetConversionMap() const
89 return m_pImpl->m_ConversionMap;
92 uno::Reference< container::XStringKeyMap > SAL_CALL SwXTextMarkup::getMarkupInfoContainer()
94 return new SwXStringKeyMap;
97 void SAL_CALL SwXTextMarkup::commitTextRangeMarkup(::sal_Int32 nType, const OUString & aIdentifier, const uno::Reference< text::XTextRange> & xRange,
98 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
100 SolarMutexGuard aGuard;
102 if (auto pRange = dynamic_cast<SwXTextRange*>(xRange.get()))
104 SwDoc& rDoc = pRange->GetDoc();
106 SwUnoInternalPaM aPam(rDoc);
108 ::sw::XTextRangeToSwPaM(aPam, xRange);
110 auto [startPos, endPos] = aPam.StartEnd(); // SwPosition*
112 commitStringMarkup (nType, aIdentifier, startPos->GetContentIndex(), endPos->GetContentIndex() - startPos->GetContentIndex(), xMarkupInfoContainer);
114 else if (auto pCursor = dynamic_cast<OTextCursorHelper*>(xRange.get()))
116 SwPaM & rPam(*pCursor->GetPaM());
118 auto [startPos, endPos] = rPam.StartEnd(); // SwPosition*
120 commitStringMarkup (nType, aIdentifier, startPos->GetContentIndex(), endPos->GetContentIndex() - startPos->GetContentIndex(), xMarkupInfoContainer);
124 void SAL_CALL SwXTextMarkup::commitStringMarkup(
125 ::sal_Int32 nType,
126 const OUString & rIdentifier,
127 ::sal_Int32 nStart,
128 ::sal_Int32 nLength,
129 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
131 SolarMutexGuard aGuard;
133 // paragraph already dead or modified?
134 if (!m_pImpl->m_pTextNode || nLength <= 0)
135 return;
137 if ( nType == text::TextMarkupType::SMARTTAG &&
138 !SwSmartTagMgr::Get().IsSmartTagTypeEnabled( rIdentifier ) )
139 return;
141 // get appropriate list to use...
142 SwWrongList* pWList = nullptr;
143 bool bRepaint = false;
144 if ( nType == text::TextMarkupType::SPELLCHECK )
146 pWList = m_pImpl->m_pTextNode->GetWrong();
147 if ( !pWList )
149 pWList = new SwWrongList( WRONGLIST_SPELL );
150 m_pImpl->m_pTextNode->SetWrong( std::unique_ptr<SwWrongList>(pWList) );
153 else if ( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
155 sw::GrammarContact* pGrammarContact = sw::getGrammarContactFor(*m_pImpl->m_pTextNode);
156 if( pGrammarContact )
158 pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true);
159 OSL_ENSURE( pWList, "GrammarContact _has_ to deliver a wrong list" );
161 else
163 pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
164 if ( !pWList )
166 m_pImpl->m_pTextNode->SetGrammarCheck( std::make_unique<SwGrammarMarkUp>() );
167 pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
170 bRepaint = pWList == m_pImpl->m_pTextNode->GetGrammarCheck();
171 if( pWList->GetBeginInv() < COMPLETE_STRING )
172 static_cast<SwGrammarMarkUp*>(pWList)->ClearGrammarList();
174 else if ( nType == text::TextMarkupType::SMARTTAG )
176 pWList = m_pImpl->m_pTextNode->GetSmartTags();
177 if ( !pWList )
179 pWList = new SwWrongList( WRONGLIST_SMARTTAG );
180 m_pImpl->m_pTextNode->SetSmartTags( std::unique_ptr<SwWrongList>(pWList) );
183 else
185 OSL_FAIL( "Unknown mark-up type" );
186 return;
189 const ModelToViewHelper::ModelPosition aStartPos =
190 m_pImpl->m_ConversionMap.ConvertToModelPosition( nStart );
191 const ModelToViewHelper::ModelPosition aEndPos =
192 m_pImpl->m_ConversionMap.ConvertToModelPosition( nStart + nLength - 1);
194 const bool bStartInField = aStartPos.mbIsField;
195 const bool bEndInField = aEndPos.mbIsField;
196 bool bCommit = false;
198 if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos )
200 nStart = aStartPos.mnSubPos;
201 const sal_Int32 nFieldPosModel = aStartPos.mnPos;
202 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
204 SwWrongList* pSubList = pWList->SubList( nInsertPos );
205 if ( !pSubList )
207 if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
208 pSubList = new SwGrammarMarkUp();
209 else
210 pSubList = new SwWrongList( pWList->GetWrongListType() );
211 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
214 pWList = pSubList;
215 bCommit = true;
217 else if ( !bStartInField && !bEndInField )
219 nStart = aStartPos.mnPos;
220 bCommit = true;
221 nLength = aEndPos.mnPos + 1 - aStartPos.mnPos;
223 else if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
225 bCommit = true;
226 nStart = aStartPos.mnPos;
227 sal_Int32 nEnd = aEndPos.mnPos;
228 if( bStartInField && nType != text::TextMarkupType::SENTENCE )
230 const sal_Int32 nFieldPosModel = aStartPos.mnPos;
231 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
232 SwWrongList* pSubList = pWList->SubList( nInsertPos );
233 if ( !pSubList )
235 pSubList = new SwGrammarMarkUp();
236 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
238 const sal_Int32 nTmpStart =
239 m_pImpl->m_ConversionMap.ConvertToViewPosition(aStartPos.mnPos);
240 const sal_Int32 nTmpLen =
241 m_pImpl->m_ConversionMap.ConvertToViewPosition(aStartPos.mnPos + 1)
242 - nTmpStart - aStartPos.mnSubPos;
243 if( nTmpLen > 0 )
245 pSubList->Insert( rIdentifier, xMarkupInfoContainer, aStartPos.mnSubPos, nTmpLen );
247 ++nStart;
249 if( bEndInField && nType != text::TextMarkupType::SENTENCE )
251 const sal_Int32 nFieldPosModel = aEndPos.mnPos;
252 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
253 SwWrongList* pSubList = pWList->SubList( nInsertPos );
254 if ( !pSubList )
256 pSubList = new SwGrammarMarkUp();
257 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
259 const sal_Int32 nTmpLen = aEndPos.mnSubPos + 1;
260 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, nTmpLen );
262 else
263 ++nEnd;
264 if( nEnd > nStart )
265 nLength = nEnd - nStart;
266 else
267 bCommit = false;
270 if ( bCommit )
272 if( nType == text::TextMarkupType::SENTENCE )
273 static_cast<SwGrammarMarkUp*>(pWList)->setSentence( nStart );
274 else
275 pWList->Insert( rIdentifier, xMarkupInfoContainer, nStart, nLength );
278 if( bRepaint )
279 sw::finishGrammarCheckFor(*m_pImpl->m_pTextNode);
282 static void lcl_commitGrammarMarkUp(
283 const ModelToViewHelper& rConversionMap,
284 SwGrammarMarkUp* pWList,
285 ::sal_Int32 nType,
286 const OUString & rIdentifier,
287 ::sal_Int32 nStart,
288 ::sal_Int32 nLength,
289 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
291 OSL_ENSURE( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE, "Wrong mark-up type" );
292 const ModelToViewHelper::ModelPosition aStartPos =
293 rConversionMap.ConvertToModelPosition( nStart );
294 const ModelToViewHelper::ModelPosition aEndPos =
295 rConversionMap.ConvertToModelPosition( nStart + nLength - 1);
297 const bool bStartInField = aStartPos.mbIsField;
298 const bool bEndInField = aEndPos.mbIsField;
299 bool bCommit = false;
301 if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos )
303 nStart = aStartPos.mnSubPos;
304 const sal_Int32 nFieldPosModel = aStartPos.mnPos;
305 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
307 SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos ));
308 if ( !pSubList )
310 pSubList = new SwGrammarMarkUp();
311 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
314 pWList = pSubList;
315 bCommit = true;
317 else if ( !bStartInField && !bEndInField )
319 nStart = aStartPos.mnPos;
320 bCommit = true;
321 nLength = aEndPos.mnPos + 1 - aStartPos.mnPos;
323 else
325 bCommit = true;
326 nStart = aStartPos.mnPos;
327 sal_Int32 nEnd = aEndPos.mnPos;
328 if( bStartInField && nType != text::TextMarkupType::SENTENCE )
330 const sal_Int32 nFieldPosModel = aStartPos.mnPos;
331 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
332 SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos ));
333 if ( !pSubList )
335 pSubList = new SwGrammarMarkUp();
336 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
338 const sal_Int32 nTmpStart = rConversionMap.ConvertToViewPosition( aStartPos.mnPos );
339 const sal_Int32 nTmpLen = rConversionMap.ConvertToViewPosition( aStartPos.mnPos + 1 )
340 - nTmpStart - aStartPos.mnSubPos;
341 if( nTmpLen > 0 )
342 pSubList->Insert( rIdentifier, xMarkupInfoContainer, aStartPos.mnSubPos, nTmpLen );
343 ++nStart;
345 if( bEndInField && nType != text::TextMarkupType::SENTENCE )
347 const sal_Int32 nFieldPosModel = aEndPos.mnPos;
348 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
349 SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos ));
350 if ( !pSubList )
352 pSubList = new SwGrammarMarkUp();
353 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
355 const sal_Int32 nTmpLen = aEndPos.mnSubPos + 1;
356 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, nTmpLen );
358 else
359 ++nEnd;
360 if( nEnd > nStart )
361 nLength = nEnd - nStart;
362 else
363 bCommit = false;
366 if ( bCommit )
368 if( nType == text::TextMarkupType::SENTENCE )
369 pWList->setSentence( nStart+nLength );
370 else
371 pWList->Insert( rIdentifier, xMarkupInfoContainer, nStart, nLength );
375 void SAL_CALL SwXTextMarkup::commitMultiTextMarkup(
376 const uno::Sequence< text::TextMarkupDescriptor > &rMarkups )
378 SolarMutexGuard aGuard;
380 // paragraph already dead or modified?
381 if (!m_pImpl->m_pTextNode)
382 return;
384 // for grammar checking there should be exactly one sentence markup
385 // and 0..n grammar markups.
386 // Different markups are not expected but may be applied anyway since
387 // that should be no problem...
388 // but it has to be implemented, at the moment only this function is for
389 // grammar markups and sentence markup only!
390 const text::TextMarkupDescriptor *pSentenceMarkUp = nullptr;
391 for( const text::TextMarkupDescriptor &rDesc : rMarkups )
393 if (rDesc.nType == text::TextMarkupType::SENTENCE)
395 if (pSentenceMarkUp != nullptr)
396 throw lang::IllegalArgumentException(); // there is already one sentence markup
397 pSentenceMarkUp = &rDesc;
399 else if( rDesc.nType != text::TextMarkupType::PROOFREADING )
400 return;
403 if( pSentenceMarkUp == nullptr )
404 return;
406 // get appropriate list to use...
407 SwGrammarMarkUp* pWList = nullptr;
408 bool bRepaint = false;
409 sw::GrammarContact* pGrammarContact = sw::getGrammarContactFor(*m_pImpl->m_pTextNode);
410 if( pGrammarContact )
412 pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true);
413 OSL_ENSURE( pWList, "GrammarContact _has_ to deliver a wrong list" );
415 else
417 pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
418 if ( !pWList )
420 m_pImpl->m_pTextNode->SetGrammarCheck( std::make_unique<SwGrammarMarkUp>() );
421 pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
422 pWList->SetInvalid( 0, COMPLETE_STRING );
425 bRepaint = pWList == m_pImpl->m_pTextNode->GetGrammarCheck();
427 bool bAcceptGrammarError = false;
428 if( pWList->GetBeginInv() < COMPLETE_STRING )
430 const ModelToViewHelper::ModelPosition aSentenceEnd =
431 m_pImpl->m_ConversionMap.ConvertToModelPosition(
432 pSentenceMarkUp->nOffset + pSentenceMarkUp->nLength );
433 bAcceptGrammarError = aSentenceEnd.mnPos > pWList->GetBeginInv();
434 pWList->ClearGrammarList( aSentenceEnd.mnPos );
437 if( bAcceptGrammarError )
439 for( const text::TextMarkupDescriptor &rDesc : rMarkups )
441 lcl_commitGrammarMarkUp(m_pImpl->m_ConversionMap, pWList, rDesc.nType,
442 rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer );
445 else
447 bRepaint = false;
448 const text::TextMarkupDescriptor &rDesc = *pSentenceMarkUp;
449 lcl_commitGrammarMarkUp(m_pImpl->m_ConversionMap, pWList, rDesc.nType,
450 rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer );
453 if( bRepaint )
454 sw::finishGrammarCheckFor(*m_pImpl->m_pTextNode);
457 void SwXTextMarkup::Impl::Notify(const SfxHint& rHint)
459 DBG_TESTSOLARMUTEX();
460 if(rHint.GetId() == SfxHintId::Dying)
462 m_pTextNode = nullptr;
466 SwXStringKeyMap::SwXStringKeyMap()
470 uno::Any SAL_CALL SwXStringKeyMap::getValue(const OUString & aKey)
472 std::map< OUString, uno::Any >::const_iterator aIter = maMap.find( aKey );
473 if ( aIter == maMap.end() )
474 throw container::NoSuchElementException();
476 return (*aIter).second;
479 sal_Bool SAL_CALL SwXStringKeyMap::hasValue(const OUString & aKey)
481 return maMap.find( aKey ) != maMap.end();
484 void SAL_CALL SwXStringKeyMap::insertValue(const OUString & aKey, const uno::Any & aValue)
486 std::map< OUString, uno::Any >::const_iterator aIter = maMap.find( aKey );
487 if ( aIter != maMap.end() )
488 throw container::ElementExistException();
490 maMap[ aKey ] = aValue;
493 ::sal_Int32 SAL_CALL SwXStringKeyMap::getCount()
495 return maMap.size();
498 OUString SAL_CALL SwXStringKeyMap::getKeyByIndex(::sal_Int32 nIndex)
500 if ( o3tl::make_unsigned(nIndex) >= maMap.size() )
501 throw lang::IndexOutOfBoundsException();
503 return OUString();
506 uno::Any SAL_CALL SwXStringKeyMap::getValueByIndex(::sal_Int32 nIndex)
508 if ( o3tl::make_unsigned(nIndex) >= maMap.size() )
509 throw lang::IndexOutOfBoundsException();
511 return uno::Any();
514 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */