tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sd / source / ui / unoidl / unosrch.cxx
blob8ed2ece29d7c53105987431bbfb301144ecd490c
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 <memory>
21 #include <sal/config.h>
23 #include <com/sun/star/drawing/XShapes.hpp>
24 #include <com/sun/star/drawing/XDrawPage.hpp>
25 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
26 #include <utility>
27 #include <vcl/svapp.hxx>
29 #include <svx/svdobj.hxx>
30 #include <svx/svdpool.hxx>
31 #include <editeng/unoipset.hxx>
32 #include <editeng/unotext.hxx>
33 #include <tools/debug.hxx>
35 #include <unoprnms.hxx>
36 #include <unosrch.hxx>
38 using namespace ::com::sun::star;
40 #define WID_SEARCH_BACKWARDS 0
41 #define WID_SEARCH_CASE 1
42 #define WID_SEARCH_WORDS 2
44 static std::span<const SfxItemPropertyMapEntry> ImplGetSearchPropertyMap()
46 static const SfxItemPropertyMapEntry aSearchPropertyMap_Impl[] =
48 { u"" UNO_NAME_SEARCH_BACKWARDS ""_ustr, WID_SEARCH_BACKWARDS, cppu::UnoType<bool>::get(), 0, 0 },
49 { u"" UNO_NAME_SEARCH_CASE ""_ustr, WID_SEARCH_CASE, cppu::UnoType<bool>::get(), 0, 0 },
50 { u"" UNO_NAME_SEARCH_WORDS ""_ustr, WID_SEARCH_WORDS, cppu::UnoType<bool>::get(), 0, 0 },
53 return aSearchPropertyMap_Impl;
56 namespace {
58 class SearchContext_impl
60 uno::Reference< drawing::XShapes > mxShapes;
61 sal_Int32 mnIndex;
63 public:
64 SearchContext_impl(uno::Reference<drawing::XShapes> xShapes)
65 : mxShapes(std::move( xShapes )), mnIndex( -1 ) {}
67 uno::Reference< drawing::XShape > firstShape()
69 mnIndex = -1;
70 return nextShape();
73 uno::Reference< drawing::XShape > nextShape()
75 uno::Reference< drawing::XShape > xShape;
76 mnIndex++;
77 if( mxShapes.is() && mxShapes->getCount() > mnIndex )
79 mxShapes->getByIndex( mnIndex ) >>= xShape;
81 return xShape;
87 /* ================================================================= */
88 /** this class implements a search or replace operation on a given
89 page or a given sdrobj
92 SdUnoSearchReplaceShape::SdUnoSearchReplaceShape( drawing::XDrawPage* pPage ) noexcept
93 : mpPage(pPage)
97 SdUnoSearchReplaceShape::~SdUnoSearchReplaceShape() noexcept
101 // util::XReplaceable
102 uno::Reference< util::XReplaceDescriptor > SAL_CALL SdUnoSearchReplaceShape::createReplaceDescriptor()
104 return new SdUnoSearchReplaceDescriptor;
107 sal_Int32 SAL_CALL SdUnoSearchReplaceShape::replaceAll( const uno::Reference< util::XSearchDescriptor >& xDesc )
109 SdUnoSearchReplaceDescriptor* pDescr = dynamic_cast<SdUnoSearchReplaceDescriptor*>( xDesc.get() );
110 if( pDescr == nullptr )
111 return 0;
113 sal_Int32 nFound = 0;
115 uno::Reference< drawing::XShapes > xShapes;
116 uno::Reference< drawing::XShape > xShape;
118 std::vector<SearchContext_impl> aContexts;
119 if(mpPage)
121 xShapes = mpPage;
123 if( xShapes->getCount() )
125 aContexts.push_back(SearchContext_impl(xShapes));
126 xShape = aContexts.back().firstShape();
128 else
130 xShapes = nullptr;
134 while( xShape.is() )
136 // replace in xShape
137 uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY);
138 uno::Reference< text::XTextRange > xRange = xText;
139 uno::Reference< text::XTextRange > xFound;
141 while( xRange.is() )
143 xFound = Search( xRange, pDescr );
144 if( !xFound.is() )
145 break;
147 xFound->setString( pDescr->getReplaceString() );
148 xRange = xFound->getEnd();
149 nFound++;
151 // done with xShape -> get next shape
153 // test if it's a group
154 uno::Reference< drawing::XShapes > xGroupShape( xShape, uno::UNO_QUERY );
155 if( xGroupShape.is() && ( xGroupShape->getCount() > 0 ) )
157 aContexts.push_back(SearchContext_impl(xGroupShape));
158 xShape = aContexts.back().firstShape();
160 else
162 if (!aContexts.empty())
163 xShape = aContexts.back().nextShape();
164 else
165 xShape = nullptr;
168 // test parent contexts for next shape if none
169 // is found in the current context
170 while (!aContexts.empty() && !xShape.is())
172 aContexts.pop_back();
173 if (!aContexts.empty())
174 xShape = aContexts.back().nextShape();
178 return nFound;
181 // XSearchable
182 uno::Reference< css::util::XSearchDescriptor > SAL_CALL SdUnoSearchReplaceShape::createSearchDescriptor( )
184 return new SdUnoSearchReplaceDescriptor;
187 uno::Reference< css::container::XIndexAccess > SAL_CALL SdUnoSearchReplaceShape::findAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc )
189 SdUnoSearchReplaceDescriptor* pDescr = dynamic_cast<SdUnoSearchReplaceDescriptor*>( xDesc.get() );
190 if( pDescr == nullptr )
191 return uno::Reference< container::XIndexAccess > ();
193 sal_Int32 nSequence = 32;
194 sal_Int32 nFound = 0;
196 uno::Sequence < uno::Reference< uno::XInterface > > aSeq( nSequence );
198 uno::Reference< uno::XInterface > * pArray = aSeq.getArray();
200 uno::Reference< drawing::XShapes > xShapes;
201 uno::Reference< drawing::XShape > xShape;
203 std::vector<SearchContext_impl> aContexts;
204 if(mpPage)
206 xShapes = mpPage;
208 if( xShapes->getCount() > 0 )
210 aContexts.push_back(SearchContext_impl(xShapes));
211 xShape = aContexts.back().firstShape();
213 else
215 xShapes = nullptr;
219 while( xShape.is() )
221 // find in xShape
222 uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY);
223 uno::Reference< text::XTextRange > xRange = xText;
224 uno::Reference< text::XTextRange > xFound;
226 while( xRange.is() )
228 xFound = Search( xRange, pDescr );
229 if( !xFound.is() )
230 break;
232 if( nFound >= nSequence )
234 nSequence += 32;
235 aSeq.realloc( nSequence );
236 pArray = aSeq.getArray();
239 pArray[nFound++] = xFound;
241 xRange = xFound->getEnd();
243 // done with shape -> get next shape
245 // test if it's a group
246 uno::Reference< drawing::XShapes > xGroupShape;
247 xGroupShape.set( xShape, uno::UNO_QUERY );
249 if( xGroupShape.is() && xGroupShape->getCount() > 0 )
251 aContexts.push_back(SearchContext_impl(xGroupShape));
252 xShape = aContexts.back().firstShape();
254 else
256 if (!aContexts.empty())
257 xShape = aContexts.back().nextShape();
258 else
259 xShape = nullptr;
262 // test parent contexts for next shape if none
263 // is found in the current context
264 while (!aContexts.empty() && !xShape.is())
266 aContexts.pop_back();
267 if (!aContexts.empty())
268 xShape = aContexts.back().nextShape();
272 if( nFound != nSequence )
273 aSeq.realloc( nFound );
275 uno::Reference<css::container::XIndexAccess> xRet(new SdUnoFindAllAccess(aSeq));
276 return xRet;
279 uno::Reference< css::uno::XInterface > SAL_CALL SdUnoSearchReplaceShape::findFirst( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc )
281 uno::Reference< text::XTextRange > xRange( GetCurrentShape(), uno::UNO_QUERY );
282 if( xRange.is() )
283 return findNext( xRange, xDesc );
285 return uno::Reference< uno::XInterface > ();
288 uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetCurrentShape() const noexcept
290 uno::Reference< drawing::XShape > xShape;
292 if( mpPage && mpPage->getCount() > 0)
293 mpPage->getByIndex(0) >>= xShape;
295 return xShape;
299 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 )
301 SdUnoSearchReplaceDescriptor* pDescr = dynamic_cast<SdUnoSearchReplaceDescriptor*>( xDesc.get() );
303 uno::Reference< uno::XInterface > xFound;
305 uno::Reference< text::XTextRange > xRange( xStartAt, uno::UNO_QUERY );
306 if(pDescr && xRange.is() )
309 uno::Reference< text::XTextRange > xCurrentRange( xStartAt, uno::UNO_QUERY );
311 uno::Reference< drawing::XShape > xCurrentShape( GetShape( xCurrentRange ) );
313 while(!xFound.is() && xRange.is())
315 xFound = Search( xRange, pDescr );
316 if(!xFound.is())
318 // we need a new starting range now
319 xRange = nullptr;
321 if(mpPage)
323 // we do a page wide search, so skip to the next shape here
324 // get next shape on our page
325 uno::Reference< drawing::XShape > xFound2( GetNextShape( mpPage, xCurrentShape ) );
326 if( xFound2.is() && (xFound2.get() != xCurrentShape.get()) )
327 xCurrentShape = std::move(xFound2);
328 else
329 xCurrentShape = nullptr;
331 xRange.set( xCurrentShape, uno::UNO_QUERY );
332 if(!(xCurrentShape.is() && (xRange.is())))
333 xRange = nullptr;
335 else
337 // we search only in this shape, so end search if we have
338 // not found anything
343 return xFound;
346 /** this method returns the shape that follows xCurrentShape in the shape collection xShapes.
347 It steps recursive into groupshapes and returns the xCurrentShape if it is the last
348 shape in this collection */
349 uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetNextShape( const uno::Reference< container::XIndexAccess >& xShapes, const uno::Reference< drawing::XShape >& xCurrentShape ) noexcept
351 uno::Reference< drawing::XShape > xFound;
353 if(xShapes.is() && xCurrentShape.is())
355 const sal_Int32 nCount = xShapes->getCount();
356 for( sal_Int32 i = 0; i < nCount; i++ )
358 uno::Reference< drawing::XShape > xSearchShape;
359 xShapes->getByIndex(i) >>= xSearchShape;
361 if( xSearchShape.is() )
363 uno::Reference< container::XIndexAccess > xGroup( xSearchShape, uno::UNO_QUERY );
365 if( xCurrentShape.get() == xSearchShape.get() )
367 if( xGroup.is() && xGroup->getCount() > 0 )
369 xGroup->getByIndex( 0 ) >>= xFound;
371 else
373 i++;
374 if( i < nCount )
375 xShapes->getByIndex( i ) >>= xFound;
376 else
377 xFound = xCurrentShape;
380 break;
382 else if( xGroup.is() )
384 xFound = GetNextShape( xGroup, xCurrentShape );
385 if( xFound.is() )
387 if( xFound.get() == xCurrentShape.get() )
389 // the current shape was found at the end of the group
390 i++;
391 if( i < nCount )
393 xShapes->getByIndex(i) >>= xFound;
396 break;
403 return xFound;
406 uno::Reference< text::XTextRange > SdUnoSearchReplaceShape::Search( const uno::Reference< text::XTextRange >& xText, SdUnoSearchReplaceDescriptor* pDescr )
408 if(!xText.is())
409 return uno::Reference< text::XTextRange > ();
411 uno::Reference< text::XText > xParent( xText->getText() );
413 if( !xParent.is() )
415 xParent.set( xText, uno::UNO_QUERY );
418 const OUString aText( xParent->getString() );
420 const sal_Int32 nTextLen = aText.getLength();
422 std::unique_ptr<sal_Int32[]> pConvertPos( new sal_Int32[nTextLen+2] );
423 std::unique_ptr<sal_Int32[]> pConvertPara( new sal_Int32[nTextLen+2] );
425 sal_Int32* pPos = pConvertPos.get();
426 sal_Int32* pPara = pConvertPara.get();
428 sal_Int32 nLastPos = 0, nLastPara = 0;
430 uno::Reference< container::XEnumerationAccess > xEnumAccess( xParent, uno::UNO_QUERY );
432 // first we fill the arrays with the position and paragraph for every character
433 // inside the text
434 if( xEnumAccess.is() )
436 uno::Reference< container::XEnumeration > xParaEnum( xEnumAccess->createEnumeration() );
438 while(xParaEnum->hasMoreElements())
440 int ndbg = 0;
441 uno::Reference< text::XTextContent > xParagraph( xParaEnum->nextElement(), uno::UNO_QUERY );
442 if( xParagraph.is() )
443 xEnumAccess.set(xParagraph, css::uno::UNO_QUERY);
444 else
445 xEnumAccess.clear();
447 if( xEnumAccess.is() )
449 uno::Reference< container::XEnumeration > xPortionEnum( xEnumAccess->createEnumeration() );
450 if( xPortionEnum.is() )
452 while(xPortionEnum->hasMoreElements())
454 uno::Reference< text::XTextRange > xPortion( xPortionEnum->nextElement(), uno::UNO_QUERY );
455 if( xPortion.is() )
457 const OUString aPortion( xPortion->getString() );
458 const sal_Int32 nLen = aPortion.getLength();
460 ESelection aStartSel( GetSelection( xPortion->getStart() ) );
461 ESelection aEndSel( GetSelection( xPortion->getEnd() ) );
463 // special case for empty portions with content or length one portions with content (fields)
464 if( (aStartSel.start.nIndex == aEndSel.start.nIndex) || ( (aStartSel.start.nIndex == (aEndSel.start.nIndex - 1)) && (nLen > 1) ) )
466 for( sal_Int32 i = 0; i < nLen; i++ )
468 if( ndbg < (nTextLen+2) )
470 *pPos++ = aStartSel.start.nIndex;
471 *pPara++ = aStartSel.start.nPara;
473 ndbg += 1;
475 else
477 OSL_FAIL( "array overflow while searching" );
481 nLastPos = aStartSel.start.nIndex;
483 // normal case
484 else
486 for( sal_Int32 i = 0; i < nLen; i++ )
488 if( ndbg < (nTextLen+2) )
490 *pPos++ = aStartSel.start.nIndex++;
491 *pPara++ = aStartSel.start.nPara;
493 ndbg += 1;
495 else
497 OSL_FAIL( "array overflow while searching" );
501 nLastPos = aStartSel.start.nIndex - 1;
502 DBG_ASSERT( aEndSel.start.nIndex == aStartSel.start.nIndex, "Search is not working" );
504 nLastPara = aStartSel.start.nPara;
510 if( ndbg < (nTextLen+2) )
512 *pPos++ = nLastPos + 1;
513 *pPara++ = nLastPara;
515 else
517 OSL_FAIL( "array overflow while searching" );
522 uno::Reference< text::XTextRange > xFound;
523 ESelection aSel;
525 if( xText.is() )
526 aSel = GetSelection( xText );
528 sal_Int32 nStartPos;
529 sal_Int32 nEndPos = 0;
530 for( nStartPos = 0; nStartPos < nTextLen; nStartPos++ )
532 if( pConvertPara[nStartPos] == aSel.start.nPara && pConvertPos[nStartPos] == aSel.start.nIndex )
533 break;
536 if( Search( aText, nStartPos, nEndPos, pDescr ) )
538 if( nStartPos <= nTextLen && nEndPos <= nTextLen )
540 ESelection aSelection( pConvertPara[nStartPos], pConvertPos[nStartPos],
541 pConvertPara[nEndPos], pConvertPos[nEndPos] );
543 SvxUnoTextBase* pParent = comphelper::getFromUnoTunnel<SvxUnoTextBase>( xParent );
545 if(pParent)
547 rtl::Reference<SvxUnoTextRange> pRange = new SvxUnoTextRange( *pParent );
548 xFound = pRange;
549 pRange->SetSelection(aSelection);
552 else
554 OSL_FAIL("Array overflow while searching!");
558 return xFound;
561 bool SdUnoSearchReplaceShape::Search( const OUString& rText, sal_Int32& nStartPos, sal_Int32& nEndPos, SdUnoSearchReplaceDescriptor* pDescr ) noexcept
563 OUString aSearchStr( pDescr->getSearchString() );
564 OUString aText( rText );
566 if( !pDescr->IsCaseSensitive() )
568 aText = aText.toAsciiLowerCase();
569 aSearchStr = aSearchStr.toAsciiLowerCase();
572 sal_Int32 nFound = aText.indexOf( aSearchStr, nStartPos );
573 if( nFound != -1 )
575 nStartPos = nFound;
576 nEndPos = nFound + aSearchStr.getLength();
578 if(pDescr->IsWords())
580 if( (nStartPos > 0 && aText[nStartPos-1] > ' ') ||
581 (nEndPos < aText.getLength() && aText[nEndPos] > ' ') )
583 nStartPos++;
584 return Search( aText, nStartPos, nEndPos, pDescr );
588 return true;
590 else
591 return false;
594 ESelection SdUnoSearchReplaceShape::GetSelection( const uno::Reference< text::XTextRange >& xTextRange ) noexcept
596 ESelection aSel;
597 SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xTextRange );
599 if(pRange)
600 aSel = pRange->GetSelection();
602 return aSel;
605 uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetShape( const uno::Reference< text::XTextRange >& xTextRange ) noexcept
607 uno::Reference< drawing::XShape > xShape;
609 if(xTextRange.is())
611 uno::Reference< text::XText > xText( xTextRange->getText() );
613 if(xText.is())
617 xShape.set( xText, uno::UNO_QUERY );
618 if(!xShape.is())
620 uno::Reference< text::XText > xParent( xText->getText() );
621 if(!xParent.is() || xText.get() == xParent.get())
622 return xShape;
624 xText = std::move(xParent);
626 } while( !xShape.is() );
630 return xShape;
633 /* ================================================================= */
634 /** this class holds the parameters and status of a search or replace
635 operation performed by class SdUnoSearchReplaceShape
638 SdUnoSearchReplaceDescriptor::SdUnoSearchReplaceDescriptor()
640 mpPropSet.reset( new SvxItemPropertySet(ImplGetSearchPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) );
642 mbBackwards = false;
643 mbCaseSensitive = false;
644 mbWords = false;
647 SdUnoSearchReplaceDescriptor::~SdUnoSearchReplaceDescriptor() noexcept
651 // XSearchDescriptor
652 OUString SAL_CALL SdUnoSearchReplaceDescriptor::getSearchString()
654 return maSearchStr;
657 void SAL_CALL SdUnoSearchReplaceDescriptor::setSearchString( const OUString& aString )
659 maSearchStr = aString;
662 // XReplaceDescriptor
663 OUString SAL_CALL SdUnoSearchReplaceDescriptor::getReplaceString()
665 return maReplaceStr;
668 void SAL_CALL SdUnoSearchReplaceDescriptor::setReplaceString( const OUString& aReplaceString )
670 maReplaceStr = aReplaceString;
673 // XPropertySet
674 uno::Reference< css::beans::XPropertySetInfo > SAL_CALL SdUnoSearchReplaceDescriptor::getPropertySetInfo()
676 SolarMutexGuard aGuard;
677 return mpPropSet->getPropertySetInfo();
680 void SAL_CALL SdUnoSearchReplaceDescriptor::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue )
682 SolarMutexGuard aGuard;
684 const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName);
686 bool bOk = false;
688 switch( pEntry ? pEntry->nWID : -1 )
690 case WID_SEARCH_BACKWARDS:
691 bOk = (aValue >>= mbBackwards);
692 break;
693 case WID_SEARCH_CASE:
694 bOk = (aValue >>= mbCaseSensitive);
695 break;
696 case WID_SEARCH_WORDS:
697 bOk = (aValue >>= mbWords);
698 break;
699 default:
700 throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this));
703 if( !bOk )
704 throw lang::IllegalArgumentException();
707 uno::Any SAL_CALL SdUnoSearchReplaceDescriptor::getPropertyValue( const OUString& PropertyName )
709 SolarMutexGuard aGuard;
711 uno::Any aAny;
713 const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName);
715 switch( pEntry ? pEntry->nWID : -1 )
717 case WID_SEARCH_BACKWARDS:
718 aAny <<= mbBackwards;
719 break;
720 case WID_SEARCH_CASE:
721 aAny <<= mbCaseSensitive;
722 break;
723 case WID_SEARCH_WORDS:
724 aAny <<= mbWords;
725 break;
726 default:
727 throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
730 return aAny;
733 void SAL_CALL SdUnoSearchReplaceDescriptor::addPropertyChangeListener( const OUString& , const css::uno::Reference< css::beans::XPropertyChangeListener >& ) {}
734 void SAL_CALL SdUnoSearchReplaceDescriptor::removePropertyChangeListener( const OUString& , const css::uno::Reference< css::beans::XPropertyChangeListener >& ) {}
735 void SAL_CALL SdUnoSearchReplaceDescriptor::addVetoableChangeListener( const OUString& , const css::uno::Reference< css::beans::XVetoableChangeListener >& ) {}
736 void SAL_CALL SdUnoSearchReplaceDescriptor::removeVetoableChangeListener( const OUString& , const css::uno::Reference< css::beans::XVetoableChangeListener >& ) {}
738 /* ================================================================= */
740 SdUnoFindAllAccess::SdUnoFindAllAccess( uno::Sequence< uno::Reference< uno::XInterface > > const & rSequence ) noexcept
741 :maSequence( rSequence )
745 SdUnoFindAllAccess::~SdUnoFindAllAccess() noexcept
749 // XElementAccess
750 uno::Type SAL_CALL SdUnoFindAllAccess::getElementType()
752 return cppu::UnoType<text::XTextRange>::get();
755 sal_Bool SAL_CALL SdUnoFindAllAccess::hasElements()
757 return maSequence.hasElements();
760 // XIndexAccess
761 sal_Int32 SAL_CALL SdUnoFindAllAccess::getCount()
763 return maSequence.getLength();
766 uno::Any SAL_CALL SdUnoFindAllAccess::getByIndex( sal_Int32 Index )
768 if( Index < 0 || Index >= getCount() )
769 throw lang::IndexOutOfBoundsException();
771 uno::Any aAny;
772 aAny <<= maSequence[Index];
773 return aAny;
776 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */