1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 #include <unotextmarkup.hxx>
27
28 #include <vos/mutex.hxx>
29 #include <vcl/svapp.hxx>
30 #include <SwSmartTagMgr.hxx>
31 #include <com/sun/star/text/TextMarkupType.hpp>
32 #include <com/sun/star/text/TextMarkupDescriptor.hpp>
33 #include <com/sun/star/container/XStringKeyMap.hpp>
34 #include <ndtxt.hxx>
35 #include <SwGrammarMarkUp.hxx>
36
37 #include <IGrammarContact.hxx>
38
39 #include <com/sun/star/lang/XUnoTunnel.hpp>
40 #include <com/sun/star/text/XTextRange.hpp>
41
42 #include <pam.hxx>
43
44 #include <unotextrange.hxx>
45 #include <unotextcursor.hxx>
46
47
48 using namespace ::com::sun::star;
49
50 /*
51 * SwXTextMarkup
52 */
SwXTextMarkup(SwTxtNode & rTxtNode,const ModelToViewHelper::ConversionMap * pMap)53 SwXTextMarkup::SwXTextMarkup( SwTxtNode& rTxtNode, const ModelToViewHelper::ConversionMap* pMap )
54 : mpTxtNode( &rTxtNode ), mpConversionMap( pMap )
55 {
56 // FME 2007-07-16 #i79641# SwXTextMarkup is allowed to be removed ...
57 SetIsAllowedToBeRemovedInModifyCall(true);
58 mpTxtNode->Add(this);
59 }
60
~SwXTextMarkup()61 SwXTextMarkup::~SwXTextMarkup()
62 {
63 delete mpConversionMap;
64 }
65
getMarkupInfoContainer()66 uno::Reference< container::XStringKeyMap > SAL_CALL SwXTextMarkup::getMarkupInfoContainer()
67 {
68 vos::OGuard aGuard(Application::GetSolarMutex());
69
70 uno::Reference< container::XStringKeyMap > xProp = new SwXStringKeyMap;
71 return xProp;
72 }
73
commitTextRangeMarkup(::sal_Int32 nType,const::rtl::OUString & aIdentifier,const uno::Reference<text::XTextRange> & xRange,const uno::Reference<container::XStringKeyMap> & xMarkupInfoContainer)74 void SAL_CALL SwXTextMarkup::commitTextRangeMarkup(::sal_Int32 nType, const ::rtl::OUString & aIdentifier, const uno::Reference< text::XTextRange> & xRange,
75 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
76 {
77 vos::OGuard aGuard(Application::GetSolarMutex());
78
79 uno::Reference<lang::XUnoTunnel> xRangeTunnel( xRange, uno::UNO_QUERY);
80
81 if(!xRangeTunnel.is()) return;
82
83 SwXTextRange* pRange = 0;
84 OTextCursorHelper* pCursor = 0;
85
86 if(xRangeTunnel.is())
87 {
88 pRange = reinterpret_cast<SwXTextRange*>( sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething(SwXTextRange::getUnoTunnelId())));
89 pCursor = reinterpret_cast<OTextCursorHelper*>( sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething(OTextCursorHelper::getUnoTunnelId())));
90 }
91
92 if (pRange)
93 {
94 SwDoc* pDoc = reinterpret_cast<SwDoc*>(pRange->GetDoc());
95
96 if (!pDoc) return;
97
98 SwUnoInternalPaM aPam(*pDoc);
99
100 ::sw::XTextRangeToSwPaM(aPam, xRange);
101
102 SwPosition* startPos = aPam.Start();
103 SwPosition* endPos = aPam.End();
104
105 commitStringMarkup (nType, aIdentifier, startPos->nContent.GetIndex(), endPos->nContent.GetIndex() - startPos->nContent.GetIndex(), xMarkupInfoContainer);
106 }
107 else if (pCursor)
108 {
109 SwPaM aPam(*pCursor->GetPaM());
110
111 SwPosition* startPos = aPam.Start();
112 SwPosition* endPos = aPam.End();
113
114 commitStringMarkup (nType, aIdentifier, startPos->nContent.GetIndex(), endPos->nContent.GetIndex() - startPos->nContent.GetIndex(), xMarkupInfoContainer);
115 }
116 }
117
118
commitStringMarkup(::sal_Int32 nType,const::rtl::OUString & rIdentifier,::sal_Int32 nStart,::sal_Int32 nLength,const uno::Reference<container::XStringKeyMap> & xMarkupInfoContainer)119 void SAL_CALL SwXTextMarkup::commitStringMarkup(
120 ::sal_Int32 nType,
121 const ::rtl::OUString & rIdentifier,
122 ::sal_Int32 nStart,
123 ::sal_Int32 nLength,
124 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
125 {
126 vos::OGuard aGuard(Application::GetSolarMutex());
127
128 // paragraph already dead or modified?
129 if ( !mpTxtNode || nLength <= 0 )
130 return;
131
132 if ( nType == text::TextMarkupType::SMARTTAG &&
133 !SwSmartTagMgr::Get().IsSmartTagTypeEnabled( rIdentifier ) )
134 return;
135
136 // get appropriate list to use...
137 SwWrongList* pWList = 0;
138 bool bRepaint = false;
139 if ( nType == text::TextMarkupType::SPELLCHECK )
140 {
141 pWList = mpTxtNode->GetWrong();
142 if ( !pWList )
143 {
144 pWList = new SwWrongList( WRONGLIST_SPELL );
145 mpTxtNode->SetWrong( pWList );
146 }
147 }
148 else if ( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
149 {
150 IGrammarContact *pGrammarContact = getGrammarContact( *mpTxtNode );
151 if( pGrammarContact )
152 {
153 pWList = pGrammarContact->getGrammarCheck( *mpTxtNode, true );
154 ASSERT( pWList, "GrammarContact _has_ to deliver a wrong list" )
155 }
156 else
157 {
158 pWList = mpTxtNode->GetGrammarCheck();
159 if ( !pWList )
160 {
161 mpTxtNode->SetGrammarCheck( new SwGrammarMarkUp() );
162 pWList = mpTxtNode->GetGrammarCheck();
163 }
164 }
165 bRepaint = pWList == mpTxtNode->GetGrammarCheck();
166 if( pWList->GetBeginInv() < STRING_LEN )
167 ((SwGrammarMarkUp*)pWList)->ClearGrammarList();
168 }
169 else if ( nType == text::TextMarkupType::SMARTTAG )
170 {
171 pWList = mpTxtNode->GetSmartTags();
172 if ( !pWList )
173 {
174 pWList = new SwWrongList( WRONGLIST_SMARTTAG );
175 mpTxtNode->SetSmartTags( pWList );
176 }
177 }
178 else
179 {
180 ASSERT( false, "Unknown mark-up type" )
181 return;
182 }
183
184
185 const ModelToViewHelper::ModelPosition aStartPos =
186 ModelToViewHelper::ConvertToModelPosition( mpConversionMap, nStart );
187 const ModelToViewHelper::ModelPosition aEndPos =
188 ModelToViewHelper::ConvertToModelPosition( mpConversionMap, nStart + nLength - 1);
189
190 const bool bStartInField = aStartPos.mbIsField;
191 const bool bEndInField = aEndPos.mbIsField;
192 bool bCommit = false;
193
194 if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos )
195 {
196 nStart = aStartPos.mnSubPos;
197 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos);
198 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
199
200 SwWrongList* pSubList = pWList->SubList( nInsertPos );
201 if ( !pSubList )
202 {
203 if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
204 pSubList = new SwGrammarMarkUp();
205 else
206 pSubList = new SwWrongList( pWList->GetWrongListType() );
207 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
208 }
209
210 pWList = pSubList;
211 bCommit = true;
212 }
213 else if ( !bStartInField && !bEndInField )
214 {
215 nStart = aStartPos.mnPos;
216 bCommit = true;
217 nLength = aEndPos.mnPos + 1 - aStartPos.mnPos;
218 }
219 else if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
220 {
221 bCommit = true;
222 nStart = aStartPos.mnPos;
223 sal_Int32 nEnd = aEndPos.mnPos;
224 if( bStartInField && nType != text::TextMarkupType::SENTENCE )
225 {
226 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos);
227 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
228 SwWrongList* pSubList = pWList->SubList( nInsertPos );
229 if ( !pSubList )
230 {
231 pSubList = new SwGrammarMarkUp();
232 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
233 }
234 const sal_uInt32 nTmpStart = ModelToViewHelper::ConvertToViewPosition( mpConversionMap, aStartPos.mnPos );
235 const sal_uInt32 nTmpLen = ModelToViewHelper::ConvertToViewPosition( mpConversionMap, aStartPos.mnPos + 1 )
236 - nTmpStart - aStartPos.mnSubPos;
237 if( nTmpLen > 0 )
238 {
239 if( nType == text::TextMarkupType::SENTENCE )
240 {
241 ((SwGrammarMarkUp*)pSubList)->setSentence( static_cast< xub_StrLen >(aStartPos.mnSubPos) );
242 bCommit = false;
243 }
244 else
245 pSubList->Insert( rIdentifier, xMarkupInfoContainer,
246 static_cast< xub_StrLen >(aStartPos.mnSubPos), static_cast< xub_StrLen >(nTmpLen) );
247 }
248 ++nStart;
249 }
250 if( bEndInField && nType != text::TextMarkupType::SENTENCE )
251 {
252 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aEndPos.mnPos);
253 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
254 SwWrongList* pSubList = pWList->SubList( nInsertPos );
255 if ( !pSubList )
256 {
257 pSubList = new SwGrammarMarkUp();
258 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
259 }
260 const sal_uInt32 nTmpLen = aEndPos.mnSubPos + 1;
261 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, static_cast< xub_StrLen >(nTmpLen) );
262 }
263 else
264 ++nEnd;
265 if( nEnd > nStart )
266 nLength = nEnd - nStart;
267 else
268 bCommit = false;
269 }
270
271 if ( bCommit )
272 {
273 if( nType == text::TextMarkupType::SENTENCE )
274 ((SwGrammarMarkUp*)pWList)->setSentence( static_cast< xub_StrLen >(nStart) );
275 else
276 pWList->Insert( rIdentifier, xMarkupInfoContainer,
277 static_cast< xub_StrLen >(nStart), static_cast< xub_StrLen >(nLength) );
278 }
279
280 if( bRepaint )
281 finishGrammarCheck( *mpTxtNode );
282 }
283
284
lcl_commitGrammarMarkUp(const ModelToViewHelper::ConversionMap * pConversionMap,SwGrammarMarkUp * pWList,::sal_Int32 nType,const::rtl::OUString & rIdentifier,::sal_Int32 nStart,::sal_Int32 nLength,const uno::Reference<container::XStringKeyMap> & xMarkupInfoContainer)285 void lcl_commitGrammarMarkUp(
286 const ModelToViewHelper::ConversionMap* pConversionMap,
287 SwGrammarMarkUp* pWList,
288 ::sal_Int32 nType,
289 const ::rtl::OUString & rIdentifier,
290 ::sal_Int32 nStart,
291 ::sal_Int32 nLength,
292 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
293 {
294 ASSERT( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE, "Wrong mark-up type" )
295 const ModelToViewHelper::ModelPosition aStartPos =
296 ModelToViewHelper::ConvertToModelPosition( pConversionMap, nStart );
297 const ModelToViewHelper::ModelPosition aEndPos =
298 ModelToViewHelper::ConvertToModelPosition( pConversionMap, nStart + nLength - 1);
299
300 const bool bStartInField = aStartPos.mbIsField;
301 const bool bEndInField = aEndPos.mbIsField;
302 bool bCommit = false;
303
304 if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos )
305 {
306 nStart = aStartPos.mnSubPos;
307 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos);
308 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
309
310 SwGrammarMarkUp* pSubList = (SwGrammarMarkUp*)pWList->SubList( nInsertPos );
311 if ( !pSubList )
312 {
313 pSubList = new SwGrammarMarkUp();
314 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
315 }
316
317 pWList = pSubList;
318 bCommit = true;
319 }
320 else if ( !bStartInField && !bEndInField )
321 {
322 nStart = aStartPos.mnPos;
323 bCommit = true;
324 nLength = aEndPos.mnPos + 1 - aStartPos.mnPos;
325 }
326 else
327 {
328 bCommit = true;
329 nStart = aStartPos.mnPos;
330 sal_Int32 nEnd = aEndPos.mnPos;
331 if( bStartInField && nType != text::TextMarkupType::SENTENCE )
332 {
333 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos);
334 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
335 SwGrammarMarkUp* pSubList = (SwGrammarMarkUp*)pWList->SubList( nInsertPos );
336 if ( !pSubList )
337 {
338 pSubList = new SwGrammarMarkUp();
339 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
340 }
341 const sal_uInt32 nTmpStart = ModelToViewHelper::ConvertToViewPosition( pConversionMap, aStartPos.mnPos );
342 const sal_uInt32 nTmpLen = ModelToViewHelper::ConvertToViewPosition( pConversionMap, aStartPos.mnPos + 1 )
343 - nTmpStart - aStartPos.mnSubPos;
344 if( nTmpLen > 0 )
345 pSubList->Insert( rIdentifier, xMarkupInfoContainer,
346 static_cast< xub_StrLen >(aStartPos.mnSubPos), static_cast< xub_StrLen >(nTmpLen) );
347 ++nStart;
348 }
349 if( bEndInField && nType != text::TextMarkupType::SENTENCE )
350 {
351 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aEndPos.mnPos);
352 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
353 SwGrammarMarkUp* pSubList = (SwGrammarMarkUp*)pWList->SubList( nInsertPos );
354 if ( !pSubList )
355 {
356 pSubList = new SwGrammarMarkUp();
357 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
358 }
359 const sal_uInt32 nTmpLen = aEndPos.mnSubPos + 1;
360 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, static_cast< xub_StrLen >(nTmpLen) );
361 }
362 else
363 ++nEnd;
364 if( nEnd > nStart )
365 nLength = nEnd - nStart;
366 else
367 bCommit = false;
368 }
369
370 if ( bCommit )
371 {
372 if( nType == text::TextMarkupType::SENTENCE )
373 ((SwGrammarMarkUp*)pWList)->setSentence( static_cast< xub_StrLen >(nStart+nLength) );
374 else
375 pWList->Insert( rIdentifier, xMarkupInfoContainer,
376 static_cast< xub_StrLen >(nStart), static_cast< xub_StrLen >(nLength) );
377 }
378 }
379
380
commitMultiTextMarkup(const uno::Sequence<text::TextMarkupDescriptor> & rMarkups)381 void SAL_CALL SwXTextMarkup::commitMultiTextMarkup(
382 const uno::Sequence< text::TextMarkupDescriptor > &rMarkups )
383 {
384 vos::OGuard aGuard(Application::GetSolarMutex());
385
386 // paragraph already dead or modified?
387 if ( !mpTxtNode )
388 return;
389
390 // check for equal length of all sequnces
391 sal_Int32 nLen = rMarkups.getLength();
392
393 // for grammar checking there should be exactly one sentence markup
394 // and 0..n grammar markups.
395 // Different markups are not expected but may be applied anyway since
396 // that should be no problem...
397 // but it has to be implemented, at the moment only this function is for
398 // grammar markups and sentence markup only!
399 sal_Int32 nSentenceMarkUpIndex = -1;
400 const text::TextMarkupDescriptor *pMarkups = rMarkups.getConstArray();
401 sal_Int32 i;
402 for( i = 0; i < nLen; ++i )
403 {
404 if (pMarkups[i].nType == text::TextMarkupType::SENTENCE)
405 {
406 if (nSentenceMarkUpIndex == -1)
407 nSentenceMarkUpIndex = i;
408 else // there is already one sentence markup
409 throw lang::IllegalArgumentException();
410 }
411 else if( pMarkups[i].nType != text::TextMarkupType::PROOFREADING )
412 return;
413 }
414
415 if( nSentenceMarkUpIndex == -1 )
416 return;
417
418 // get appropriate list to use...
419 SwGrammarMarkUp* pWList = 0;
420 bool bRepaint = false;
421 IGrammarContact *pGrammarContact = getGrammarContact( *mpTxtNode );
422 if( pGrammarContact )
423 {
424 pWList = pGrammarContact->getGrammarCheck( *mpTxtNode, true );
425 ASSERT( pWList, "GrammarContact _has_ to deliver a wrong list" )
426 }
427 else
428 {
429 pWList = mpTxtNode->GetGrammarCheck();
430 if ( !pWList )
431 {
432 mpTxtNode->SetGrammarCheck( new SwGrammarMarkUp() );
433 pWList = mpTxtNode->GetGrammarCheck();
434 pWList->SetInvalid( 0, STRING_LEN );
435 }
436 }
437 bRepaint = pWList == mpTxtNode->GetGrammarCheck();
438
439 bool bAcceptGrammarError = false;
440 if( pWList->GetBeginInv() < STRING_LEN )
441 {
442 const ModelToViewHelper::ModelPosition aSentenceEnd =
443 ModelToViewHelper::ConvertToModelPosition( mpConversionMap,
444 pMarkups[nSentenceMarkUpIndex].nOffset + pMarkups[nSentenceMarkUpIndex].nLength );
445 bAcceptGrammarError = (xub_StrLen)aSentenceEnd.mnPos > pWList->GetBeginInv();
446 pWList->ClearGrammarList( (xub_StrLen)aSentenceEnd.mnPos );
447 }
448
449 if( bAcceptGrammarError )
450 {
451 for( i = 0; i < nLen; ++i )
452 {
453 const text::TextMarkupDescriptor &rDesc = pMarkups[i];
454 lcl_commitGrammarMarkUp( mpConversionMap, pWList, rDesc.nType,
455 rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer );
456 }
457 }
458 else
459 {
460 bRepaint = false;
461 i = nSentenceMarkUpIndex;
462 const text::TextMarkupDescriptor &rDesc = pMarkups[i];
463 lcl_commitGrammarMarkUp( mpConversionMap, pWList, rDesc.nType,
464 rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer );
465 }
466
467 if( bRepaint )
468 finishGrammarCheck( *mpTxtNode );
469
470 return;
471 }
472
473
Modify(const SfxPoolItem *,const SfxPoolItem *)474 void SwXTextMarkup::Modify( const SfxPoolItem* /*pOld*/, const SfxPoolItem* /*pNew*/ )
475 {
476 // FME 2007-07-16 #i79641# In my opinion this is perfectly legal,
477 // therefore I remove the assertion in SwModify::_Remove()
478 if ( GetRegisteredIn() )
479 GetRegisteredInNonConst()->Remove( this );
480 // <--
481
482 vos::OGuard aGuard(Application::GetSolarMutex());
483 mpTxtNode = 0;
484 }
485
486 /*
487 * SwXStringKeyMap
488 */
SwXStringKeyMap()489 SwXStringKeyMap::SwXStringKeyMap()
490 {
491 }
492
getValue(const::rtl::OUString & aKey)493 uno::Any SAL_CALL SwXStringKeyMap::getValue(const ::rtl::OUString & aKey)
494 {
495 std::map< rtl::OUString, uno::Any >::const_iterator aIter = maMap.find( aKey );
496 if ( aIter == maMap.end() )
497 throw container::NoSuchElementException();
498
499 return (*aIter).second;
500 }
501
hasValue(const::rtl::OUString & aKey)502 ::sal_Bool SAL_CALL SwXStringKeyMap::hasValue(const ::rtl::OUString & aKey)
503 {
504 return maMap.find( aKey ) != maMap.end();
505 }
506
insertValue(const::rtl::OUString & aKey,const uno::Any & aValue)507 void SAL_CALL SwXStringKeyMap::insertValue(const ::rtl::OUString & aKey, const uno::Any & aValue)
508 {
509 std::map< rtl::OUString, uno::Any >::const_iterator aIter = maMap.find( aKey );
510 if ( aIter != maMap.end() )
511 throw container::ElementExistException();
512
513 maMap[ aKey ] = aValue;
514 }
515
getCount()516 ::sal_Int32 SAL_CALL SwXStringKeyMap::getCount()
517 {
518 return maMap.size();
519 }
520
getKeyByIndex(::sal_Int32 nIndex)521 ::rtl::OUString SAL_CALL SwXStringKeyMap::getKeyByIndex(::sal_Int32 nIndex)
522 {
523 if ( (sal_uInt32)nIndex >= maMap.size() )
524 throw lang::IndexOutOfBoundsException();
525
526 return ::rtl::OUString();
527 }
528
getValueByIndex(::sal_Int32 nIndex)529 uno::Any SAL_CALL SwXStringKeyMap::getValueByIndex(::sal_Int32 nIndex)
530 {
531 if ( (sal_uInt32)nIndex >= maMap.size() )
532 throw lang::IndexOutOfBoundsException();
533
534 return uno::Any();
535 }
536