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 
27 #include <editeng/unolingu.hxx>
28 
29 #include <unobaseclass.hxx>
30 #include <unoflatpara.hxx>
31 
32 #include <vos/mutex.hxx>
33 #include <vcl/svapp.hxx>
34 #include <com/sun/star/text/TextMarkupType.hpp>
35 #include <unotextmarkup.hxx>
36 #include <ndtxt.hxx>
37 #include <doc.hxx>
38 #include <docsh.hxx>
39 #include <viewsh.hxx>
40 #include <viewimp.hxx>
41 #include <breakit.hxx>
42 #include <pam.hxx>
43 #include <unobaseclass.hxx>
44 #include <unotextrange.hxx>
45 #include <pagefrm.hxx>
46 #include <cntfrm.hxx>
47 #include <rootfrm.hxx>
48 #include <poolfmt.hxx>
49 #include <pagedesc.hxx>
50 #include <IGrammarContact.hxx>
51 #include <viewopt.hxx>
52 
53 using namespace ::com::sun::star;
54 
55 /******************************************************************************
56  * SwXFlatParagraph
57  ******************************************************************************/
58 
59 SwXFlatParagraph::SwXFlatParagraph( SwTxtNode& rTxtNode, rtl::OUString aExpandText, const ModelToViewHelper::ConversionMap* pMap ) :
60     SwXTextMarkup( rTxtNode, pMap ),
61     maExpandText( aExpandText )
62 {
63 }
64 
65 SwXFlatParagraph::~SwXFlatParagraph()
66 {
67 }
68 
69 uno::Sequence< uno::Type > SwXFlatParagraph::getTypes(  ) throw(uno::RuntimeException)
70 {
71 	uno::Sequence< uno::Type > aTypes = SwXTextMarkup::getTypes();
72 	aTypes.realloc( aTypes.getLength() + 1 );
73     aTypes[aTypes.getLength()-1] = ::getCppuType((uno::Reference< text::XFlatParagraph >*)0);
74 	return aTypes;
75 }
76 
77 uno::Sequence< sal_Int8 > SwXFlatParagraph::getImplementationId(  ) throw(uno::RuntimeException)
78 {
79     vos::OGuard aGuard(Application::GetSolarMutex());
80     static uno::Sequence< sal_Int8 > aId( 16 );
81     static sal_Bool bInit = sal_False;
82     if(!bInit)
83     {
84         rtl_createUuid( (sal_uInt8 *)(aId.getArray() ), 0, sal_True );
85         bInit = sal_True;
86     }
87     return aId;
88 }
89 
90 uno::Any SAL_CALL SwXFlatParagraph::queryInterface( const uno::Type& rType ) throw(uno::RuntimeException)
91 {
92 	if ( rType == ::getCppuType((uno::Reference< text::XFlatParagraph >*)0) )
93 	{
94 		return uno::makeAny( uno::Reference < text::XFlatParagraph >(this) );
95 	}
96 	else
97 		return SwXTextMarkup::queryInterface( rType );
98 }
99 
100 void SAL_CALL SwXFlatParagraph::acquire() throw()
101 {
102     SwXTextMarkup::acquire();
103 }
104 
105 void SAL_CALL SwXFlatParagraph::release() throw()
106 {
107     SwXTextMarkup::release();
108 }
109 
110 const SwTxtNode* SwXFlatParagraph::getTxtNode() const
111 {
112     return mpTxtNode;
113 }
114 
115 css::uno::Reference< css::container::XStringKeyMap > SAL_CALL SwXFlatParagraph::getMarkupInfoContainer() throw (css::uno::RuntimeException)
116 {
117     return SwXTextMarkup::getMarkupInfoContainer();
118 }
119 
120 void SAL_CALL SwXFlatParagraph::commitTextMarkup(::sal_Int32 nType, const ::rtl::OUString & rIdentifier, ::sal_Int32 nStart, ::sal_Int32 nLength, const css::uno::Reference< css::container::XStringKeyMap > & rxMarkupInfoContainer) throw (css::uno::RuntimeException)
121 {
122     vos::OGuard aGuard(Application::GetSolarMutex());
123     SwXTextMarkup::commitTextMarkup( nType, rIdentifier, nStart, nLength,  rxMarkupInfoContainer );
124 }
125 
126 // text::XFlatParagraph:
127 ::rtl::OUString SAL_CALL SwXFlatParagraph::getText() throw (uno::RuntimeException)
128 {
129     return maExpandText;
130 }
131 
132 // text::XFlatParagraph:
133 void SAL_CALL SwXFlatParagraph::setChecked( ::sal_Int32 nType, ::sal_Bool bVal ) throw (uno::RuntimeException)
134 {
135     vos::OGuard aGuard(Application::GetSolarMutex());
136 
137     if ( mpTxtNode )
138     {
139         if ( text::TextMarkupType::SPELLCHECK == nType )
140             mpTxtNode->SetWrongDirty( !bVal );
141         else if ( text::TextMarkupType::SMARTTAG == nType )
142             mpTxtNode->SetSmartTagDirty( !bVal );
143         else if( text::TextMarkupType::PROOFREADING == nType )
144         {
145             mpTxtNode->SetGrammarCheckDirty( !bVal );
146             if( bVal )
147                 ::finishGrammarCheck( *mpTxtNode );
148         }
149     }
150 }
151 
152 // text::XFlatParagraph:
153 ::sal_Bool SAL_CALL SwXFlatParagraph::isChecked( ::sal_Int32 nType ) throw (uno::RuntimeException)
154 {
155     vos::OGuard aGuard(Application::GetSolarMutex());
156     if ( mpTxtNode )
157     {
158         if ( text::TextMarkupType::SPELLCHECK == nType )
159             return mpTxtNode->IsWrongDirty();
160         else if ( text::TextMarkupType::PROOFREADING == nType )
161             return mpTxtNode->IsGrammarCheckDirty();
162         else if ( text::TextMarkupType::SMARTTAG == nType )
163             return mpTxtNode->IsSmartTagDirty();
164     }
165 
166     return sal_False;
167 }
168 
169 // text::XFlatParagraph:
170 ::sal_Bool SAL_CALL SwXFlatParagraph::isModified() throw (uno::RuntimeException)
171 {
172     vos::OGuard aGuard(Application::GetSolarMutex());
173     return 0 == mpTxtNode;
174 }
175 
176 // text::XFlatParagraph:
177 lang::Locale SAL_CALL SwXFlatParagraph::getLanguageOfText(::sal_Int32 nPos, ::sal_Int32 nLen)
178 	throw (uno::RuntimeException, lang::IllegalArgumentException)
179 {
180     vos::OGuard aGuard(Application::GetSolarMutex());
181     if (!mpTxtNode)
182         return SvxCreateLocale( LANGUAGE_NONE );
183 
184     const lang::Locale aLocale( SW_BREAKITER()->GetLocale( mpTxtNode->GetLang( static_cast<sal_uInt16>(nPos), static_cast<sal_uInt16>(nLen) ) ) );
185     return aLocale;
186 }
187 
188 // text::XFlatParagraph:
189 lang::Locale SAL_CALL SwXFlatParagraph::getPrimaryLanguageOfText(::sal_Int32 nPos, ::sal_Int32 nLen)
190 	throw (uno::RuntimeException, lang::IllegalArgumentException)
191 {
192     vos::OGuard aGuard(Application::GetSolarMutex());
193 
194     if (!mpTxtNode)
195         return SvxCreateLocale( LANGUAGE_NONE );
196 
197     const lang::Locale aLocale( SW_BREAKITER()->GetLocale( mpTxtNode->GetLang( static_cast<sal_uInt16>(nPos), static_cast<sal_uInt16>(nLen) ) ) );
198     return aLocale;
199 }
200 
201 // text::XFlatParagraph:
202 void SAL_CALL SwXFlatParagraph::changeText(::sal_Int32 nPos, ::sal_Int32 nLen, const ::rtl::OUString & aNewText, const css::uno::Sequence< css::beans::PropertyValue > & aAttributes) throw (css::uno::RuntimeException, css::lang::IllegalArgumentException)
203 {
204     vos::OGuard aGuard(Application::GetSolarMutex());
205 
206     if ( !mpTxtNode )
207         return;
208 
209     SwTxtNode* pOldTxtNode = mpTxtNode;
210 
211     SwPaM aPaM( *mpTxtNode, static_cast<sal_uInt16>(nPos), *mpTxtNode, static_cast<sal_uInt16>(nPos + nLen) );
212 
213     UnoActionContext aAction( mpTxtNode->GetDoc() );
214 
215     const uno::Reference< text::XTextRange > xRange =
216         SwXTextRange::CreateXTextRange(
217             *mpTxtNode->GetDoc(), *aPaM.GetPoint(), aPaM.GetMark() );
218     uno::Reference< beans::XPropertySet > xPropSet( xRange, uno::UNO_QUERY );
219     if ( xPropSet.is() )
220     {
221         for ( sal_uInt16 i = 0; i < aAttributes.getLength(); ++i )
222             xPropSet->setPropertyValue( aAttributes[i].Name, aAttributes[i].Value );
223     }
224 
225     mpTxtNode = pOldTxtNode; // setPropertyValue() modifies this. We restore the old state.
226 
227     IDocumentContentOperations* pIDCO = mpTxtNode->getIDocumentContentOperations();
228     pIDCO->ReplaceRange( aPaM, aNewText, false );
229 
230     mpTxtNode = 0;
231 }
232 
233 // text::XFlatParagraph:
234 void SAL_CALL SwXFlatParagraph::changeAttributes(::sal_Int32 nPos, ::sal_Int32 nLen, const css::uno::Sequence< css::beans::PropertyValue > & aAttributes) throw (css::uno::RuntimeException, css::lang::IllegalArgumentException)
235 {
236     vos::OGuard aGuard(Application::GetSolarMutex());
237 
238     if ( !mpTxtNode )
239         return;
240 
241     SwPaM aPaM( *mpTxtNode, static_cast<sal_uInt16>(nPos), *mpTxtNode, static_cast<sal_uInt16>(nPos + nLen) );
242 
243     UnoActionContext aAction( mpTxtNode->GetDoc() );
244 
245     const uno::Reference< text::XTextRange > xRange =
246         SwXTextRange::CreateXTextRange(
247             *mpTxtNode->GetDoc(), *aPaM.GetPoint(), aPaM.GetMark() );
248     uno::Reference< beans::XPropertySet > xPropSet( xRange, uno::UNO_QUERY );
249     if ( xPropSet.is() )
250     {
251         for ( sal_uInt16 i = 0; i < aAttributes.getLength(); ++i )
252             xPropSet->setPropertyValue( aAttributes[i].Name, aAttributes[i].Value );
253     }
254 
255     mpTxtNode = 0;
256 }
257 
258 // text::XFlatParagraph:
259 css::uno::Sequence< ::sal_Int32 > SAL_CALL SwXFlatParagraph::getLanguagePortions() throw (css::uno::RuntimeException)
260 {
261     vos::OGuard aGuard(Application::GetSolarMutex());
262     return css::uno::Sequence< ::sal_Int32>();
263 }
264 
265 
266 const uno::Sequence< sal_Int8 >&
267 SwXFlatParagraph::getUnoTunnelId()
268 {
269     static uno::Sequence<sal_Int8> aSeq(CreateUnoTunnelId());
270     return aSeq;
271 }
272 
273 
274 sal_Int64 SAL_CALL
275 SwXFlatParagraph::getSomething(
276         const uno::Sequence< sal_Int8 >& rId)
277     throw (uno::RuntimeException)
278 {
279     return sw::UnoTunnelImpl(rId, this);
280 }
281 
282 
283 /******************************************************************************
284  * SwXFlatParagraphIterator
285  ******************************************************************************/
286 
287 SwXFlatParagraphIterator::SwXFlatParagraphIterator( SwDoc& rDoc, sal_Int32 nType, sal_Bool bAutomatic )
288     : mpDoc( &rDoc ),
289       mnType( nType ),
290       mbAutomatic( bAutomatic ),
291       mnCurrentNode( 0 ),
292       mnStartNode( 0 ),
293       mnEndNode( rDoc.GetNodes().Count() ),
294       mbWrapped( sal_False )
295 {
296     //mnStartNode = mnCurrentNode = get node from current cursor TODO!
297 
298     // register as listener and get notified when document is closed
299     mpDoc->GetPageDescFromPool( RES_POOLPAGE_STANDARD )->Add(this);
300 }
301 
302 SwXFlatParagraphIterator::~SwXFlatParagraphIterator()
303 {
304 }
305 
306 
307 void SwXFlatParagraphIterator::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew )
308 {
309     ClientModify( this, pOld, pNew );
310     // check if document gets closed...
311     if(!GetRegisteredIn())
312     {
313         vos::OGuard aGuard(Application::GetSolarMutex());
314         mpDoc = 0;
315     }
316 }
317 
318 
319 uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getFirstPara()
320 	throw( uno::RuntimeException )
321 {
322     return getNextPara();   // TODO
323 }
324 
325 uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getNextPara()
326 	throw( uno::RuntimeException )
327 {
328     vos::OGuard aGuard(Application::GetSolarMutex());
329 
330     uno::Reference< text::XFlatParagraph > xRet;
331     if (!mpDoc)
332         return xRet;
333 
334     SwTxtNode* pRet = 0;
335     if ( mbAutomatic )
336     {
337         ViewShell* pViewShell = 0;
338         mpDoc->GetEditShell( &pViewShell );
339 
340         SwPageFrm* pCurrentPage = pViewShell ? pViewShell->Imp()->GetFirstVisPage() : 0;
341         SwPageFrm* pStartPage = pCurrentPage;
342         SwPageFrm* pStopPage = 0;
343 
344         while ( pCurrentPage != pStopPage )
345         {
346             if (mnType != text::TextMarkupType::SPELLCHECK || pCurrentPage->IsInvalidSpelling() )
347             {
348 				// this method is supposed to return an empty paragraph in case Online Checking is disabled
349 				if ( ( mnType == text::TextMarkupType::PROOFREADING || mnType == text::TextMarkupType::SPELLCHECK )
350 					&& !pViewShell->GetViewOptions()->IsOnlineSpell() )
351 					return xRet;
352 
353                 // search for invalid content:
354                 SwCntntFrm* pCnt = pCurrentPage->ContainsCntnt();
355 
356                 while( pCnt && pCurrentPage->IsAnLower( pCnt ) )
357                 {
358                     SwTxtNode* pTxtNode = dynamic_cast<SwTxtNode*>( pCnt->GetNode()->GetTxtNode() );
359 
360                     if ( pTxtNode &&
361                         ((mnType == text::TextMarkupType::SPELLCHECK &&
362                                 pTxtNode->IsWrongDirty()) ||
363                          (mnType == text::TextMarkupType::PROOFREADING &&
364                                 pTxtNode->IsGrammarCheckDirty())) )
365                     {
366                         pRet = pTxtNode;
367                         break;
368                     }
369 
370                     pCnt = pCnt->GetNextCntntFrm();
371                 }
372             }
373 
374             if ( pRet )
375                 break;
376 
377             // if there is no invalid text node on the current page,
378             // we validate the page
379             pCurrentPage->ValidateSpelling();
380 
381             // proceed with next page, wrap at end of document if required:
382             pCurrentPage = static_cast<SwPageFrm*>(pCurrentPage->GetNext());
383 
384             if ( !pCurrentPage && !pStopPage )
385             {
386                 pStopPage = pStartPage;
387                 pCurrentPage = static_cast<SwPageFrm*>(pViewShell->GetLayout()->Lower());
388             }
389         }
390     }
391     else    // non-automatic checking
392     {
393         const SwNodes& rNodes = mpDoc->GetNodes();
394         const sal_uLong nMaxNodes = rNodes.Count();
395 
396         while ( mnCurrentNode < mnEndNode && mnCurrentNode < nMaxNodes )
397         {
398             SwNode* pNd = rNodes[ mnCurrentNode ];
399 
400             ++mnCurrentNode;
401 
402             pRet = dynamic_cast<SwTxtNode*>(pNd);
403             if ( pRet )
404                 break;
405 
406             if ( mnCurrentNode == mnEndNode && !mbWrapped )
407             {
408                 mnCurrentNode = 0;
409                 mnEndNode = mnStartNode;
410             }
411         }
412     }
413 
414     if ( pRet )
415     {
416         // Expand the string:
417         rtl::OUString aExpandText;
418         const ModelToViewHelper::ConversionMap* pConversionMap =
419                 pRet->BuildConversionMap( aExpandText );
420 
421         xRet = new SwXFlatParagraph( *pRet, aExpandText, pConversionMap );
422 		// keep hard references...
423 		m_aFlatParaList.insert( xRet );
424     }
425 
426 	return xRet;
427 }
428 
429 uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getLastPara()
430 	throw( uno::RuntimeException )
431 {
432     return getNextPara();
433 }
434 
435 uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getParaAfter(const uno::Reference< text::XFlatParagraph > & xPara)
436 	throw ( uno::RuntimeException, lang::IllegalArgumentException )
437 {
438     vos::OGuard aGuard(Application::GetSolarMutex());
439 
440     uno::Reference< text::XFlatParagraph > xRet;
441     if (!mpDoc)
442         return xRet;
443 
444     const uno::Reference<lang::XUnoTunnel> xFPTunnel(xPara, uno::UNO_QUERY);
445     OSL_ASSERT(xFPTunnel.is());
446     SwXFlatParagraph* const pFlatParagraph(sw::UnoTunnelGetImplementation<SwXFlatParagraph>(xFPTunnel));
447 
448     if ( !pFlatParagraph )
449         return xRet;
450 
451     const SwTxtNode* pCurrentNode = pFlatParagraph->getTxtNode();
452 
453     if ( !pCurrentNode )
454         return xRet;
455 
456     SwTxtNode* pNextTxtNode = 0;
457     const SwNodes& rNodes = pCurrentNode->GetDoc()->GetNodes();
458 
459     for( sal_uLong nCurrentNode = pCurrentNode->GetIndex() + 1; nCurrentNode < rNodes.Count(); ++nCurrentNode )
460     {
461         SwNode* pNd = rNodes[ nCurrentNode ];
462         pNextTxtNode = dynamic_cast<SwTxtNode*>(pNd);
463         if ( pNextTxtNode )
464             break;
465     }
466 
467     if ( pNextTxtNode )
468     {
469         // Expand the string:
470         rtl::OUString aExpandText;
471         const ModelToViewHelper::ConversionMap* pConversionMap =
472                 pNextTxtNode->BuildConversionMap( aExpandText );
473 
474         xRet = new SwXFlatParagraph( *pNextTxtNode, aExpandText, pConversionMap );
475 		// keep hard references...
476 		m_aFlatParaList.insert( xRet );
477     }
478 
479     return xRet;
480 }
481 
482 uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getParaBefore(const uno::Reference< text::XFlatParagraph > & xPara )
483 	throw ( uno::RuntimeException, lang::IllegalArgumentException )
484 {
485     vos::OGuard aGuard(Application::GetSolarMutex());
486 
487     uno::Reference< text::XFlatParagraph > xRet;
488     if (!mpDoc)
489         return xRet;
490 
491     const uno::Reference<lang::XUnoTunnel> xFPTunnel(xPara, uno::UNO_QUERY);
492     OSL_ASSERT(xFPTunnel.is());
493     SwXFlatParagraph* const pFlatParagraph(sw::UnoTunnelGetImplementation<SwXFlatParagraph>(xFPTunnel));
494 
495     if ( !pFlatParagraph )
496         return xRet;
497 
498     const SwTxtNode* pCurrentNode = pFlatParagraph->getTxtNode();
499 
500     if ( !pCurrentNode )
501         return xRet;
502 
503     SwTxtNode* pPrevTxtNode = 0;
504     const SwNodes& rNodes = pCurrentNode->GetDoc()->GetNodes();
505 
506     for( sal_uLong nCurrentNode = pCurrentNode->GetIndex() - 1; nCurrentNode > 0; --nCurrentNode )
507     {
508         SwNode* pNd = rNodes[ nCurrentNode ];
509         pPrevTxtNode = dynamic_cast<SwTxtNode*>(pNd);
510         if ( pPrevTxtNode )
511             break;
512     }
513 
514     if ( pPrevTxtNode )
515     {
516         // Expand the string:
517         rtl::OUString aExpandText;
518         const ModelToViewHelper::ConversionMap* pConversionMap =
519                 pPrevTxtNode->BuildConversionMap( aExpandText );
520 
521         xRet = new SwXFlatParagraph( *pPrevTxtNode, aExpandText, pConversionMap );
522 		// keep hard references...
523 		m_aFlatParaList.insert( xRet );
524     }
525 
526     return xRet;
527 }
528