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 .
21 #include <sal/config.h>
23 #include <com/sun/star/drawing/XShapes.hpp>
24 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
25 #include <vcl/svapp.hxx>
27 #include <svx/svdobj.hxx>
28 #include <svx/svdpool.hxx>
29 #include <editeng/unoipset.hxx>
30 #include <editeng/unotext.hxx>
31 #include <tools/debug.hxx>
33 #include <unoprnms.hxx>
34 #include <unosrch.hxx>
36 using namespace ::com::sun::star
;
38 #define WID_SEARCH_BACKWARDS 0
39 #define WID_SEARCH_CASE 1
40 #define WID_SEARCH_WORDS 2
42 static const SfxItemPropertyMapEntry
* ImplGetSearchPropertyMap()
44 static const SfxItemPropertyMapEntry aSearchPropertyMap_Impl
[] =
46 { OUString(UNO_NAME_SEARCH_BACKWARDS
), WID_SEARCH_BACKWARDS
, cppu::UnoType
<bool>::get(), 0, 0 },
47 { OUString(UNO_NAME_SEARCH_CASE
), WID_SEARCH_CASE
, cppu::UnoType
<bool>::get(), 0, 0 },
48 { OUString(UNO_NAME_SEARCH_WORDS
), WID_SEARCH_WORDS
, cppu::UnoType
<bool>::get(), 0, 0 },
49 { OUString(), 0, css::uno::Type(), 0, 0 }
52 return aSearchPropertyMap_Impl
;
55 class SearchContext_impl
57 uno::Reference
< drawing::XShapes
> mxShapes
;
61 SearchContext_impl(uno::Reference
<drawing::XShapes
> const& xShapes
)
62 : mxShapes( xShapes
), mnIndex( -1 ) {}
64 uno::Reference
< drawing::XShape
> firstShape()
70 uno::Reference
< drawing::XShape
> nextShape()
72 uno::Reference
< drawing::XShape
> xShape
;
74 if( mxShapes
.is() && mxShapes
->getCount() > mnIndex
)
76 mxShapes
->getByIndex( mnIndex
) >>= xShape
;
82 /* ================================================================= */
83 /** this class implements a search or replace operation on a given
84 page or a given sdrobj
87 SdUnoSearchReplaceShape::SdUnoSearchReplaceShape( drawing::XDrawPage
* pPage
) throw()
92 SdUnoSearchReplaceShape::~SdUnoSearchReplaceShape() throw()
97 uno::Reference
< util::XReplaceDescriptor
> SAL_CALL
SdUnoSearchReplaceShape::createReplaceDescriptor()
99 return new SdUnoSearchReplaceDescriptor
;
102 sal_Int32 SAL_CALL
SdUnoSearchReplaceShape::replaceAll( const uno::Reference
< util::XSearchDescriptor
>& xDesc
)
104 SdUnoSearchReplaceDescriptor
* pDescr
= comphelper::getUnoTunnelImplementation
<SdUnoSearchReplaceDescriptor
>( xDesc
);
105 if( pDescr
== nullptr )
108 sal_Int32 nFound
= 0;
110 uno::Reference
< drawing::XShapes
> xShapes
;
111 uno::Reference
< drawing::XShape
> xShape
;
113 std::vector
<SearchContext_impl
> aContexts
;
116 uno::Reference
< drawing::XDrawPage
> xPage( mpPage
);
118 xShapes
.set( xPage
, uno::UNO_QUERY
);
120 if( xShapes
.is() && (xShapes
->getCount() > 0) )
122 aContexts
.push_back(SearchContext_impl(xShapes
));
123 xShape
= aContexts
.back().firstShape();
134 uno::Reference
< text::XText
> xText(xShape
, uno::UNO_QUERY
);
135 uno::Reference
< text::XTextRange
> xRange
= xText
;
136 uno::Reference
< text::XTextRange
> xFound
;
140 xFound
= Search( xRange
, pDescr
);
144 xFound
->setString( pDescr
->getReplaceString() );
145 xRange
= xFound
->getEnd();
148 // done with xShape -> get next shape
150 // test if it's a group
151 uno::Reference
< drawing::XShapes
> xGroupShape( xShape
, uno::UNO_QUERY
);
152 if( xGroupShape
.is() && ( xGroupShape
->getCount() > 0 ) )
154 aContexts
.push_back(SearchContext_impl(xGroupShape
));
155 xShape
= aContexts
.back().firstShape();
159 if (!aContexts
.empty())
160 xShape
= aContexts
.back().nextShape();
165 // test parent contexts for next shape if none
166 // is found in the current context
167 while (!aContexts
.empty() && !xShape
.is())
169 aContexts
.pop_back();
170 if (!aContexts
.empty())
171 xShape
= aContexts
.back().nextShape();
179 uno::Reference
< css::util::XSearchDescriptor
> SAL_CALL
SdUnoSearchReplaceShape::createSearchDescriptor( )
181 return new SdUnoSearchReplaceDescriptor
;
184 uno::Reference
< css::container::XIndexAccess
> SAL_CALL
SdUnoSearchReplaceShape::findAll( const css::uno::Reference
< css::util::XSearchDescriptor
>& xDesc
)
186 SdUnoSearchReplaceDescriptor
* pDescr
= comphelper::getUnoTunnelImplementation
<SdUnoSearchReplaceDescriptor
>( xDesc
);
187 if( pDescr
== nullptr )
188 return uno::Reference
< container::XIndexAccess
> ();
190 sal_Int32 nSequence
= 32;
191 sal_Int32 nFound
= 0;
193 uno::Sequence
< uno::Reference
< uno::XInterface
> > aSeq( nSequence
);
195 uno::Reference
< uno::XInterface
> * pArray
= aSeq
.getArray();
197 uno::Reference
< drawing::XShapes
> xShapes
;
198 uno::Reference
< drawing::XShape
> xShape
;
200 std::vector
<SearchContext_impl
> aContexts
;
203 uno::Reference
< drawing::XDrawPage
> xPage( mpPage
);
204 xShapes
.set( xPage
, uno::UNO_QUERY
);
206 if( xShapes
.is() && xShapes
->getCount() > 0 )
208 aContexts
.push_back(SearchContext_impl(xShapes
));
209 xShape
= aContexts
.back().firstShape();
220 uno::Reference
< text::XText
> xText(xShape
, uno::UNO_QUERY
);
221 uno::Reference
< text::XTextRange
> xRange
= xText
;
222 uno::Reference
< text::XTextRange
> xFound
;
226 xFound
= Search( xRange
, pDescr
);
230 if( nFound
>= nSequence
)
233 aSeq
.realloc( nSequence
);
234 pArray
= aSeq
.getArray();
237 pArray
[nFound
++] = xFound
;
239 xRange
= xFound
->getEnd();
241 // done with shape -> get next shape
243 // test if it's a group
244 uno::Reference
< drawing::XShapes
> xGroupShape
;
245 xGroupShape
.set( xShape
, uno::UNO_QUERY
);
247 if( xGroupShape
.is() && xGroupShape
->getCount() > 0 )
249 aContexts
.push_back(SearchContext_impl(xGroupShape
));
250 xShape
= aContexts
.back().firstShape();
254 if (!aContexts
.empty())
255 xShape
= aContexts
.back().nextShape();
260 // test parent contexts for next shape if none
261 // is found in the current context
262 while (!aContexts
.empty() && !xShape
.is())
264 aContexts
.pop_back();
265 if (!aContexts
.empty())
266 xShape
= aContexts
.back().nextShape();
270 if( nFound
!= nSequence
)
271 aSeq
.realloc( nFound
);
273 uno::Reference
<css::container::XIndexAccess
> xRet(new SdUnoFindAllAccess(aSeq
));
277 uno::Reference
< css::uno::XInterface
> SAL_CALL
SdUnoSearchReplaceShape::findFirst( const css::uno::Reference
< css::util::XSearchDescriptor
>& xDesc
)
279 uno::Reference
< text::XTextRange
> xRange( GetCurrentShape(), uno::UNO_QUERY
);
281 return findNext( xRange
, xDesc
);
283 return uno::Reference
< uno::XInterface
> ();
286 uno::Reference
< drawing::XShape
> SdUnoSearchReplaceShape::GetCurrentShape() const throw()
288 uno::Reference
< drawing::XShape
> xShape
;
292 uno::Reference
< drawing::XDrawPage
> xPage( mpPage
);
293 uno::Reference
< container::XIndexAccess
> xShapes( xPage
, uno::UNO_QUERY
);
296 if(xShapes
->getCount() > 0)
298 xShapes
->getByIndex(0) >>= xShape
;
307 uno::Reference
< css::uno::XInterface
> SAL_CALL
SdUnoSearchReplaceShape::findNext( const css::uno::Reference
< css::uno::XInterface
>& xStartAt
, const css::uno::Reference
< css::util::XSearchDescriptor
>& xDesc
)
309 SdUnoSearchReplaceDescriptor
* pDescr
= comphelper::getUnoTunnelImplementation
<SdUnoSearchReplaceDescriptor
>( xDesc
);
311 uno::Reference
< uno::XInterface
> xFound
;
313 uno::Reference
< text::XTextRange
> xRange( xStartAt
, uno::UNO_QUERY
);
314 if(pDescr
&& xRange
.is() )
317 uno::Reference
< text::XTextRange
> xCurrentRange( xStartAt
, uno::UNO_QUERY
);
319 uno::Reference
< drawing::XShape
> xCurrentShape( GetShape( xCurrentRange
) );
321 while(!xFound
.is() && xRange
.is())
323 xFound
= Search( xRange
, pDescr
);
326 // we need a new starting range now
331 uno::Reference
< drawing::XDrawPage
> xPage( mpPage
);
333 // we do a page wide search, so skip to the next shape here
334 uno::Reference
< container::XIndexAccess
> xShapes( xPage
, uno::UNO_QUERY
);
336 // get next shape on our page
339 uno::Reference
< drawing::XShape
> xFound2( GetNextShape( xShapes
, xCurrentShape
) );
340 if( xFound2
.is() && (xFound2
.get() != xCurrentShape
.get()) )
341 xCurrentShape
= xFound2
;
343 xCurrentShape
= nullptr;
345 xRange
.set( xCurrentShape
, uno::UNO_QUERY
);
346 if(!(xCurrentShape
.is() && (xRange
.is())))
352 // we search only in this shape, so end search if we have
353 // not found anything
361 /** this method returns the shape that follows xCurrentShape in the shape collection xShapes.
362 It steps recursive into groupshapes and returns the xCurrentShape if it is the last
363 shape in this collection */
364 uno::Reference
< drawing::XShape
> SdUnoSearchReplaceShape::GetNextShape( const uno::Reference
< container::XIndexAccess
>& xShapes
, const uno::Reference
< drawing::XShape
>& xCurrentShape
) throw()
366 uno::Reference
< drawing::XShape
> xFound
;
368 if(xShapes
.is() && xCurrentShape
.is())
370 const sal_Int32 nCount
= xShapes
->getCount();
371 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
373 uno::Reference
< drawing::XShape
> xSearchShape
;
374 xShapes
->getByIndex(i
) >>= xSearchShape
;
376 if( xSearchShape
.is() )
378 uno::Reference
< container::XIndexAccess
> xGroup( xSearchShape
, uno::UNO_QUERY
);
380 if( xCurrentShape
.get() == xSearchShape
.get() )
382 if( xGroup
.is() && xGroup
->getCount() > 0 )
384 xGroup
->getByIndex( 0 ) >>= xFound
;
390 xShapes
->getByIndex( i
) >>= xFound
;
392 xFound
= xCurrentShape
;
397 else if( xGroup
.is() )
399 xFound
= GetNextShape( xGroup
, xCurrentShape
);
402 if( xFound
.get() == xCurrentShape
.get() )
404 // the current shape was found at the end of the group
408 xShapes
->getByIndex(i
) >>= xFound
;
421 uno::Reference
< text::XTextRange
> SdUnoSearchReplaceShape::Search( const uno::Reference
< text::XTextRange
>& xText
, SdUnoSearchReplaceDescriptor
* pDescr
)
424 return uno::Reference
< text::XTextRange
> ();
426 uno::Reference
< text::XText
> xParent( xText
->getText() );
430 xParent
.set( xText
, uno::UNO_QUERY
);
433 const OUString
aText( xParent
->getString() );
435 const sal_Int32 nTextLen
= aText
.getLength();
437 std::unique_ptr
<sal_Int32
[]> pConvertPos( new sal_Int32
[nTextLen
+2] );
438 std::unique_ptr
<sal_Int32
[]> pConvertPara( new sal_Int32
[nTextLen
+2] );
440 const sal_Unicode
* pText
= aText
.getStr();
442 sal_Int32
* pPos
= pConvertPos
.get();
443 sal_Int32
* pPara
= pConvertPara
.get();
445 sal_Int32 nLastPos
= 0, nLastPara
= 0;
447 uno::Reference
< container::XEnumerationAccess
> xEnumAccess( xParent
, uno::UNO_QUERY
);
449 // first we fill the arrays with the position and paragraph for every character
451 if( xEnumAccess
.is() )
453 uno::Reference
< container::XEnumeration
> xParaEnum( xEnumAccess
->createEnumeration() );
455 while(xParaEnum
->hasMoreElements())
458 uno::Reference
< text::XTextContent
> xParagraph( xParaEnum
->nextElement(), uno::UNO_QUERY
);
459 if( xParagraph
.is() )
460 xEnumAccess
.set(xParagraph
, css::uno::UNO_QUERY
);
464 if( xEnumAccess
.is() )
466 uno::Reference
< container::XEnumeration
> xPortionEnum( xEnumAccess
->createEnumeration() );
467 if( xPortionEnum
.is() )
469 while(xPortionEnum
->hasMoreElements())
471 uno::Reference
< text::XTextRange
> xPortion( xPortionEnum
->nextElement(), uno::UNO_QUERY
);
474 const OUString
aPortion( xPortion
->getString() );
475 const sal_Int32 nLen
= aPortion
.getLength();
477 ESelection
aStartSel( GetSelection( xPortion
->getStart() ) );
478 ESelection
aEndSel( GetSelection( xPortion
->getEnd() ) );
480 // special case for empty portions with content or length one portions with content (fields)
481 if( (aStartSel
.nStartPos
== aEndSel
.nStartPos
) || ( (aStartSel
.nStartPos
== (aEndSel
.nStartPos
- 1)) && (nLen
> 1) ) )
483 for( sal_Int32 i
= 0; i
< nLen
; i
++ )
485 if( ndbg
< (nTextLen
+2) )
487 *pPos
++ = aStartSel
.nStartPos
;
488 *pPara
++ = aStartSel
.nStartPara
;
495 OSL_FAIL( "array overflow while searching" );
499 nLastPos
= aStartSel
.nStartPos
;
504 for( sal_Int32 i
= 0; i
< nLen
; i
++ )
506 if( ndbg
< (nTextLen
+2) )
508 *pPos
++ = aStartSel
.nStartPos
++;
509 *pPara
++ = aStartSel
.nStartPara
;
516 OSL_FAIL( "array overflow while searching" );
520 nLastPos
= aStartSel
.nStartPos
- 1;
521 DBG_ASSERT( aEndSel
.nStartPos
== aStartSel
.nStartPos
, "Search is not working" );
523 nLastPara
= aStartSel
.nStartPara
;
529 if( ndbg
< (nTextLen
+2) )
531 *pPos
++ = nLastPos
+ 1;
532 *pPara
++ = nLastPara
;
538 OSL_FAIL( "array overflow while searching" );
543 uno::Reference
< text::XTextRange
> xFound
;
547 aSel
= GetSelection( xText
);
550 sal_Int32 nEndPos
= 0;
551 for( nStartPos
= 0; nStartPos
< nTextLen
; nStartPos
++ )
553 if( pConvertPara
[nStartPos
] == aSel
.nStartPara
&& pConvertPos
[nStartPos
] == aSel
.nStartPos
)
557 if( Search( aText
, nStartPos
, nEndPos
, pDescr
) )
559 if( nStartPos
<= nTextLen
&& nEndPos
<= nTextLen
)
561 ESelection
aSelection( pConvertPara
[nStartPos
], pConvertPos
[nStartPos
],
562 pConvertPara
[nEndPos
], pConvertPos
[nEndPos
] );
563 SvxUnoTextRange
*pRange
;
565 SvxUnoTextBase
* pParent
= comphelper::getUnoTunnelImplementation
<SvxUnoTextBase
>( xParent
);
569 pRange
= new SvxUnoTextRange( *pParent
);
571 pRange
->SetSelection(aSelection
);
577 OSL_FAIL("Array overflow while searching!");
584 bool SdUnoSearchReplaceShape::Search( const OUString
& rText
, sal_Int32
& nStartPos
, sal_Int32
& nEndPos
, SdUnoSearchReplaceDescriptor
* pDescr
) throw()
586 OUString
aSearchStr( pDescr
->getSearchString() );
587 OUString
aText( rText
);
589 if( !pDescr
->IsCaseSensitive() )
591 aText
= aText
.toAsciiLowerCase();
592 aSearchStr
= aSearchStr
.toAsciiLowerCase();
595 sal_Int32 nFound
= aText
.indexOf( aSearchStr
, nStartPos
);
599 nEndPos
= nFound
+ aSearchStr
.getLength();
601 if(pDescr
->IsWords())
603 if( (nStartPos
> 0 && aText
[nStartPos
-1] > ' ') ||
604 (nEndPos
< aText
.getLength() && aText
[nEndPos
] > ' ') )
607 return Search( aText
, nStartPos
, nEndPos
, pDescr
);
617 ESelection
SdUnoSearchReplaceShape::GetSelection( const uno::Reference
< text::XTextRange
>& xTextRange
) throw()
620 SvxUnoTextRangeBase
* pRange
= comphelper::getUnoTunnelImplementation
<SvxUnoTextRangeBase
>( xTextRange
);
623 aSel
= pRange
->GetSelection();
628 uno::Reference
< drawing::XShape
> SdUnoSearchReplaceShape::GetShape( const uno::Reference
< text::XTextRange
>& xTextRange
) throw()
630 uno::Reference
< drawing::XShape
> xShape
;
634 uno::Reference
< text::XText
> xText( xTextRange
->getText() );
640 xShape
.set( xText
, uno::UNO_QUERY
);
643 uno::Reference
< text::XText
> xParent( xText
->getText() );
644 if(!xParent
.is() || xText
.get() == xParent
.get())
649 } while( !xShape
.is() );
656 /* ================================================================= */
657 /** this class holds the parameters and status of a search or replace
658 operation performed by class SdUnoSearchReplaceShape
661 UNO3_GETIMPLEMENTATION_IMPL( SdUnoSearchReplaceDescriptor
);
663 SdUnoSearchReplaceDescriptor::SdUnoSearchReplaceDescriptor()
665 mpPropSet
.reset( new SvxItemPropertySet(ImplGetSearchPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) );
668 mbCaseSensitive
= false;
672 SdUnoSearchReplaceDescriptor::~SdUnoSearchReplaceDescriptor() throw()
677 OUString SAL_CALL
SdUnoSearchReplaceDescriptor::getSearchString()
682 void SAL_CALL
SdUnoSearchReplaceDescriptor::setSearchString( const OUString
& aString
)
684 maSearchStr
= aString
;
687 // XReplaceDescriptor
688 OUString SAL_CALL
SdUnoSearchReplaceDescriptor::getReplaceString()
693 void SAL_CALL
SdUnoSearchReplaceDescriptor::setReplaceString( const OUString
& aReplaceString
)
695 maReplaceStr
= aReplaceString
;
699 uno::Reference
< css::beans::XPropertySetInfo
> SAL_CALL
SdUnoSearchReplaceDescriptor::getPropertySetInfo()
701 SolarMutexGuard aGuard
;
702 return mpPropSet
->getPropertySetInfo();
705 void SAL_CALL
SdUnoSearchReplaceDescriptor::setPropertyValue( const OUString
& aPropertyName
, const css::uno::Any
& aValue
)
707 SolarMutexGuard aGuard
;
709 const SfxItemPropertySimpleEntry
* pEntry
= mpPropSet
->getPropertyMapEntry(aPropertyName
);
713 switch( pEntry
? pEntry
->nWID
: -1 )
715 case WID_SEARCH_BACKWARDS
:
716 bOk
= (aValue
>>= mbBackwards
);
718 case WID_SEARCH_CASE
:
719 bOk
= (aValue
>>= mbCaseSensitive
);
721 case WID_SEARCH_WORDS
:
722 bOk
= (aValue
>>= mbWords
);
725 throw beans::UnknownPropertyException( aPropertyName
, static_cast<cppu::OWeakObject
*>(this));
729 throw lang::IllegalArgumentException();
732 uno::Any SAL_CALL
SdUnoSearchReplaceDescriptor::getPropertyValue( const OUString
& PropertyName
)
734 SolarMutexGuard aGuard
;
738 const SfxItemPropertySimpleEntry
* pEntry
= mpPropSet
->getPropertyMapEntry(PropertyName
);
740 switch( pEntry
? pEntry
->nWID
: -1 )
742 case WID_SEARCH_BACKWARDS
:
743 aAny
<<= mbBackwards
;
745 case WID_SEARCH_CASE
:
746 aAny
<<= mbCaseSensitive
;
748 case WID_SEARCH_WORDS
:
752 throw beans::UnknownPropertyException( PropertyName
, static_cast<cppu::OWeakObject
*>(this));
758 void SAL_CALL
SdUnoSearchReplaceDescriptor::addPropertyChangeListener( const OUString
& , const css::uno::Reference
< css::beans::XPropertyChangeListener
>& ) {}
759 void SAL_CALL
SdUnoSearchReplaceDescriptor::removePropertyChangeListener( const OUString
& , const css::uno::Reference
< css::beans::XPropertyChangeListener
>& ) {}
760 void SAL_CALL
SdUnoSearchReplaceDescriptor::addVetoableChangeListener( const OUString
& , const css::uno::Reference
< css::beans::XVetoableChangeListener
>& ) {}
761 void SAL_CALL
SdUnoSearchReplaceDescriptor::removeVetoableChangeListener( const OUString
& , const css::uno::Reference
< css::beans::XVetoableChangeListener
>& ) {}
763 /* ================================================================= */
765 SdUnoFindAllAccess::SdUnoFindAllAccess( uno::Sequence
< uno::Reference
< uno::XInterface
> > const & rSequence
) throw()
766 :maSequence( rSequence
)
770 SdUnoFindAllAccess::~SdUnoFindAllAccess() throw()
775 uno::Type SAL_CALL
SdUnoFindAllAccess::getElementType()
777 return cppu::UnoType
<text::XTextRange
>::get();
780 sal_Bool SAL_CALL
SdUnoFindAllAccess::hasElements()
782 return maSequence
.hasElements();
786 sal_Int32 SAL_CALL
SdUnoFindAllAccess::getCount()
788 return maSequence
.getLength();
791 uno::Any SAL_CALL
SdUnoFindAllAccess::getByIndex( sal_Int32 Index
)
793 if( Index
< 0 || Index
>= getCount() )
794 throw lang::IndexOutOfBoundsException();
797 aAny
<<= maSequence
[Index
];
801 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */