1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <Outliner.hxx>
21 #include <boost/property_tree/json_parser.hpp>
22 #include <vcl/settings.hxx>
23 #include <vcl/svapp.hxx>
24 
25 #include <svl/srchitem.hxx>
26 #include <svl/intitem.hxx>
27 #include <editeng/editstat.hxx>
28 #include <vcl/canvastools.hxx>
29 #include <vcl/outdev.hxx>
30 #include <vcl/weld.hxx>
31 #include <sfx2/dispatch.hxx>
32 #include <svx/svdotext.hxx>
33 #include <svx/svdograf.hxx>
34 #include <editeng/unolingu.hxx>
35 #include <com/sun/star/linguistic2/XSpellChecker1.hpp>
36 #include <svx/srchdlg.hxx>
37 #include <unotools/linguprops.hxx>
38 #include <unotools/lingucfg.hxx>
39 #include <editeng/editeng.hxx>
40 #include <sfx2/viewfrm.hxx>
41 #include <tools/debug.hxx>
42 #include <tools/diagnose_ex.h>
43 
44 #include <strings.hrc>
45 #include <editeng/outliner.hxx>
46 #include <sdmod.hxx>
47 #include <Window.hxx>
48 #include <sdresid.hxx>
49 #include <DrawViewShell.hxx>
50 #include <OutlineView.hxx>
51 #include <OutlineViewShell.hxx>
52 #include <drawdoc.hxx>
53 #include <DrawDocShell.hxx>
54 #include <drawview.hxx>
55 #include <ViewShellBase.hxx>
56 #include <SpellDialogChildWindow.hxx>
57 #include <framework/FrameworkHelper.hxx>
58 #include <svx/svxids.hrc>
59 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
60 #include <comphelper/string.hxx>
61 #include <comphelper/lok.hxx>
62 #include <comphelper/scopeguard.hxx>
63 #include <VectorGraphicSearchContext.hxx>
64 #include <fusearch.hxx>
65 
66 using namespace ::com::sun::star;
67 using namespace ::com::sun::star::uno;
68 using namespace ::com::sun::star::lang;
69 using namespace ::com::sun::star::linguistic2;
70 
71 class SfxStyleSheetPool;
72 
73 class SdOutliner::Implementation
74 {
75 public:
76     /** The original edit mode directly after switching to a different view
77         mode.  Used for restoring the edit mode when leaving that view mode
78         again.
79     */
80     EditMode meOriginalEditMode;
81 
82     Implementation();
83     ~Implementation();
84 
85     /** Return the OutlinerView that was provided by the last call to
86         ProvideOutlinerView() (or NULL when there was no such call.)
87     */
GetOutlinerView()88     OutlinerView* GetOutlinerView() { return mpOutlineView;}
89 
90     /** Provide in the member mpOutlineView an instance of OutlinerView that
91         is either taken from the ViewShell, when it is an OutlineViewShell,
92         or is created.  When an OutlinerView already exists it is initialized.
93     */
94     void ProvideOutlinerView (
95         Outliner& rOutliner,
96         const std::shared_ptr<sd::ViewShell>& rpViewShell,
97         vcl::Window* pWindow);
98 
99     /** This method is called when the OutlinerView is no longer used.
100     */
101     void ReleaseOutlinerView();
102 
getVectorGraphicSearchContext()103     sd::VectorGraphicSearchContext& getVectorGraphicSearchContext() { return maVectorGraphicSearchContext; }
104 
105 private:
106     /** Flag that specifies whether we own the outline view pointed to by
107         <member>mpOutlineView</member> and thus have to
108         delete it in <member>EndSpelling()</member>.
109     */
110     bool mbOwnOutlineView;
111 
112     /** The outline view used for searching and spelling.  If searching or
113         spell checking an outline view this data member points to that view.
114         For all other views an instance is created.  The
115         <member>mbOwnOutlineView</member> distinguishes between both cases.
116     */
117     OutlinerView* mpOutlineView;
118 
119     sd::VectorGraphicSearchContext maVectorGraphicSearchContext;
120 };
121 
122 namespace
123 {
124 
getViewShellBase()125 sd::ViewShellBase* getViewShellBase()
126 {
127     return dynamic_cast<sd::ViewShellBase*>(SfxViewShell::Current());
128 }
129 
130 } // end anonymous namespace
131 
SdOutliner(SdDrawDocument * pDoc,OutlinerMode nMode)132 SdOutliner::SdOutliner( SdDrawDocument* pDoc, OutlinerMode nMode )
133     : SdrOutliner( &pDoc->GetItemPool(), nMode ),
134       mpImpl(new Implementation()),
135       meMode(SEARCH),
136       mpView(nullptr),
137       mpWeakViewShell(),
138       mpWindow(nullptr),
139       mpDrawDocument(pDoc),
140       mnConversionLanguage(LANGUAGE_NONE),
141       mnIgnoreCurrentPageChangesLevel(0),
142       mbStringFound(false),
143       mbMatchMayExist(false),
144       mnPageCount(0),
145       mbEndOfSearch(false),
146       mbFoundObject(false),
147       mbDirectionIsForward(true),
148       mbRestrictSearchToSelection(false),
149       maMarkListCopy(),
150       mpObj(nullptr),
151       mpFirstObj(nullptr),
152       mpSearchSpellTextObj(nullptr),
153       mnText(0),
154       mpParaObj(nullptr),
155       meStartViewMode(PageKind::Standard),
156       meStartEditMode(EditMode::Page),
157       mnStartPageIndex(sal_uInt16(-1)),
158       mpStartEditedObject(nullptr),
159       maStartSelection(),
160       mpSearchItem(nullptr),
161       maObjectIterator(),
162       maCurrentPosition(),
163       maSearchStartPosition(),
164       maLastValidPosition(),
165       mbPrepareSpellingPending(true)
166 {
167     SetStyleSheetPool(static_cast<SfxStyleSheetPool*>( mpDrawDocument->GetStyleSheetPool() ));
168     SetEditTextObjectPool( &pDoc->GetItemPool() );
169     SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl));
170     SetForbiddenCharsTable( pDoc->GetForbiddenCharsTable() );
171 
172     EEControlBits nCntrl = GetControlWord();
173     nCntrl |= EEControlBits::ALLOWBIGOBJS;
174     nCntrl |= EEControlBits::MARKFIELDS;
175     nCntrl |= EEControlBits::AUTOCORRECT;
176 
177     bool bOnlineSpell = false;
178 
179     sd::DrawDocShell* pDocSh = mpDrawDocument->GetDocSh();
180 
181     if (pDocSh)
182     {
183         bOnlineSpell = mpDrawDocument->GetOnlineSpell();
184     }
185     else
186     {
187         bOnlineSpell = false;
188 
189         try
190         {
191             const SvtLinguConfig    aLinguConfig;
192             Any aAny = aLinguConfig.GetProperty( UPN_IS_SPELL_AUTO );
193             aAny >>= bOnlineSpell;
194         }
195         catch( ... )
196         {
197             OSL_FAIL( "Ill. type in linguistic property" );
198         }
199     }
200 
201     if (bOnlineSpell)
202         nCntrl |= EEControlBits::ONLINESPELLING;
203     else
204         nCntrl &= ~EEControlBits::ONLINESPELLING;
205 
206     SetControlWord(nCntrl);
207 
208     Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() );
209     if ( xSpellChecker.is() )
210         SetSpeller( xSpellChecker );
211 
212     Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() );
213     if( xHyphenator.is() )
214         SetHyphenator( xHyphenator );
215 
216     SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
217 }
218 
219 /// Nothing spectacular in the destructor.
~SdOutliner()220 SdOutliner::~SdOutliner()
221 {
222 }
223 
getOutlinerView()224 OutlinerView* SdOutliner::getOutlinerView()
225 {
226     return mpImpl->GetOutlinerView();
227 }
228 
229 /** Prepare find&replace or spellchecking.  This distinguishes between three
230     cases:
231     <ol>
232     <li>The current shell is a <type>DrawViewShell</type>: Create a
233     <type>OutlinerView</type> object and search all objects of (i) the
234     current mark list, (ii) of the current view, or (iii) of all the view
235     combinations:
236     <ol>
237     <li>Draw view, slide view</li>
238     <li>Draw view, background view</li>
239     <li>Notes view, slide view</li>
240     <li>Notes view, background view</li>
241     <li>Handout view, slide view</li>
242     <li>Handout view, background view</li>
243     </ol>
244 
245     <li>When the current shell is a <type>SdOutlineViewShell</type> then
246     directly operate on it.  No switching into other views takes place.</li>
247     </ol>
248 */
PrepareSpelling()249 void SdOutliner::PrepareSpelling()
250 {
251     mbPrepareSpellingPending = false;
252 
253     sd::ViewShellBase* pBase = getViewShellBase();
254     if (pBase != nullptr)
255         SetViewShell (pBase->GetMainViewShell());
256     SetRefDevice( SD_MOD()->GetVirtualRefDevice() );
257 
258     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
259     if (pViewShell)
260     {
261         mbStringFound = false;
262 
263         // Supposed that we are not located at the very beginning/end of
264         // the document then there may be a match in the document
265         // prior/after the current position.
266         mbMatchMayExist = true;
267 
268         maObjectIterator = sd::outliner::Iterator();
269         maSearchStartPosition = sd::outliner::Iterator();
270         RememberStartPosition();
271 
272         mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow);
273 
274         HandleChangedSelection ();
275     }
276     ClearModifyFlag();
277 }
278 
StartSpelling()279 void SdOutliner::StartSpelling()
280 {
281     meMode = SPELL;
282     mbDirectionIsForward = true;
283     mpSearchItem = nullptr;
284 }
285 
286 /** Free all resources acquired during the search/spell check.  After a
287     spell check the start position is restored here.
288 */
EndSpelling()289 void SdOutliner::EndSpelling()
290 {
291     // Keep old view shell alive until we release the outliner view.
292     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
293     std::shared_ptr<sd::ViewShell> pOldViewShell (pViewShell);
294 
295     sd::ViewShellBase* pBase = getViewShellBase();
296     if (pBase != nullptr)
297         pViewShell = pBase->GetMainViewShell();
298     else
299         pViewShell.reset();
300     mpWeakViewShell = pViewShell;
301 
302     // When in <member>PrepareSpelling()</member> a new outline view has
303     // been created then delete it here.
304     bool bViewIsDrawViewShell(dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ));
305     if (bViewIsDrawViewShell)
306     {
307         SetStatusEventHdl(Link<EditStatus&,void>());
308         mpView = pViewShell->GetView();
309         mpView->UnmarkAllObj (mpView->GetSdrPageView());
310         mpView->SdrEndTextEdit();
311         // Make FuSelection the current function.
312         pViewShell->GetDispatcher()->Execute(
313             SID_OBJECT_SELECT,
314             SfxCallMode::SYNCHRON | SfxCallMode::RECORD);
315 
316         // Remove and, if previously created by us, delete the outline
317         // view.
318         OutlinerView* pOutlinerView = getOutlinerView();
319         if (pOutlinerView != nullptr)
320         {
321             RemoveView(pOutlinerView);
322             mpImpl->ReleaseOutlinerView();
323         }
324 
325         SetUpdateMode(true);
326     }
327 
328     // Before clearing the modify flag use it as a hint that
329     // changes were done at SpellCheck
330     if(IsModified())
331     {
332         if(auto pOutlineView = dynamic_cast<sd::OutlineView *>( mpView ))
333             pOutlineView->PrepareClose();
334         if(mpDrawDocument && !mpDrawDocument->IsChanged())
335             mpDrawDocument->SetChanged();
336     }
337 
338     // Now clear the modify flag to have a specified state of
339     // Outliner
340     ClearModifyFlag();
341 
342     // When spell checking then restore the start position.
343     if (meMode==SPELL || meMode==TEXT_CONVERSION)
344         RestoreStartPosition ();
345 
346     mpWeakViewShell.reset();
347     mpView = nullptr;
348     mpWindow = nullptr;
349     mnStartPageIndex = sal_uInt16(-1);
350 }
351 
SpellNextDocument()352 bool SdOutliner::SpellNextDocument()
353 {
354     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
355     if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
356     {
357         // When doing a spell check in the outline view then there is
358         // only one document.
359         mbEndOfSearch = true;
360         EndOfSearch ();
361     }
362     else
363     {
364         if( auto pOutlineView = dynamic_cast<sd::OutlineView *>( mpView ))
365             pOutlineView->PrepareClose();
366         mpDrawDocument->GetDocSh()->SetWaitCursor( true );
367 
368         Initialize (true);
369 
370         mpWindow = pViewShell->GetActiveWindow();
371         OutlinerView* pOutlinerView = getOutlinerView();
372         if (pOutlinerView != nullptr)
373             pOutlinerView->SetWindow(mpWindow);
374         ProvideNextTextObject ();
375 
376         mpDrawDocument->GetDocSh()->SetWaitCursor( false );
377         ClearModifyFlag();
378     }
379 
380     return !mbEndOfSearch;
381 }
382 
383 /**
384  * check next text object
385  */
GetNextSpellSentence()386 svx::SpellPortions SdOutliner::GetNextSpellSentence()
387 {
388     svx::SpellPortions aResult;
389 
390     DetectChange();
391     // Iterate over sentences and text shapes until a sentence with a
392     // spelling error has been found.  If no such sentence can be
393     // found the loop is left through a break.
394     // It is the responsibility of the sd outliner object to correctly
395     // iterate over all text shapes, i.e. switch between views, wrap
396     // around at the end of the document, stop when all text shapes
397     // have been examined exactly once.
398     bool bFoundNextSentence = false;
399     while ( ! bFoundNextSentence)
400     {
401         OutlinerView* pOutlinerView = GetView(0);
402         if (pOutlinerView != nullptr)
403         {
404             ESelection aCurrentSelection (pOutlinerView->GetSelection());
405             if ( ! mbMatchMayExist
406                 && maStartSelection < aCurrentSelection)
407                 EndOfSearch();
408 
409             // Advance to the next sentence.
410             bFoundNextSentence = SpellSentence( pOutlinerView->GetEditView(), aResult);
411         }
412 
413         // When no sentence with spelling errors has been found in the
414         // currently selected text shape or there is no selected text
415         // shape then advance to the next text shape.
416         if ( ! bFoundNextSentence)
417             if ( ! SpellNextDocument())
418                 // All text objects have been processed so exit the
419                 // loop and return an empty portions list.
420                 break;
421     }
422 
423     return aResult;
424 }
425 
426 /** Go to next match.
427 */
StartSearchAndReplace(const SvxSearchItem * pSearchItem)428 bool SdOutliner::StartSearchAndReplace (const SvxSearchItem* pSearchItem)
429 {
430     bool bEndOfSearch = true;
431 
432     // clear the search toolbar entry
433     SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
434 
435     mpDrawDocument->GetDocSh()->SetWaitCursor( true );
436     if (mbPrepareSpellingPending)
437         PrepareSpelling();
438     sd::ViewShellBase* pBase = getViewShellBase();
439     // Determine whether we have to abort the search.  This is necessary
440     // when the main view shell does not support searching.
441     bool bAbort = false;
442     if (pBase != nullptr)
443     {
444         std::shared_ptr<sd::ViewShell> pShell (pBase->GetMainViewShell());
445         SetViewShell(pShell);
446         if (pShell == nullptr)
447             bAbort = true;
448         else
449             switch (pShell->GetShellType())
450             {
451                 case sd::ViewShell::ST_DRAW:
452                 case sd::ViewShell::ST_IMPRESS:
453                 case sd::ViewShell::ST_NOTES:
454                 case sd::ViewShell::ST_HANDOUT:
455                 case sd::ViewShell::ST_OUTLINE:
456                     bAbort = false;
457                     break;
458                 default:
459                     bAbort = true;
460                     break;
461             }
462     }
463 
464     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
465     if ( ! pViewShell)
466     {
467         OSL_ASSERT(pViewShell);
468         return true;
469     }
470 
471     if ( ! bAbort)
472     {
473         meMode = SEARCH;
474         mpSearchItem = pSearchItem;
475 
476         mbFoundObject = false;
477 
478         Initialize ( ! mpSearchItem->GetBackward());
479 
480         const SvxSearchCmd nCommand (mpSearchItem->GetCommand());
481         if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
482         {
483             bEndOfSearch = SearchAndReplaceAll ();
484         }
485         else
486         {
487             RememberStartPosition ();
488             bEndOfSearch = SearchAndReplaceOnce ();
489             // restore start position if nothing was found
490             if(!mbStringFound)
491             {
492                 RestoreStartPosition ();
493                 // Nothing was changed, no need to restart the spellchecker.
494                 if (nCommand == SvxSearchCmd::FIND)
495                     bEndOfSearch = false;
496             }
497             mnStartPageIndex = sal_uInt16(-1);
498         }
499     }
500 
501     mpDrawDocument->GetDocSh()->SetWaitCursor( false );
502 
503     return bEndOfSearch;
504 }
505 
Initialize(bool bDirectionIsForward)506 void SdOutliner::Initialize (bool bDirectionIsForward)
507 {
508     const bool bIsAtEnd (maObjectIterator == sd::outliner::OutlinerContainer(this).end());
509     const bool bOldDirectionIsForward = mbDirectionIsForward;
510     mbDirectionIsForward = bDirectionIsForward;
511 
512     if (maObjectIterator == sd::outliner::Iterator())
513     {
514         // Initialize a new search.
515         maObjectIterator = sd::outliner::OutlinerContainer(this).current();
516         maCurrentPosition = *maObjectIterator;
517 
518         std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
519         if ( ! pViewShell)
520         {
521             OSL_ASSERT(pViewShell);
522             return;
523         }
524 
525         // In case we are searching in an outline view then first remove the
526         // current selection and place cursor at its start or end.
527         if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
528         {
529             ESelection aSelection = getOutlinerView()->GetSelection ();
530             if (mbDirectionIsForward)
531             {
532                 aSelection.nEndPara = aSelection.nStartPara;
533                 aSelection.nEndPos = aSelection.nStartPos;
534             }
535             else
536             {
537                 aSelection.nStartPara = aSelection.nEndPara;
538                 aSelection.nStartPos = aSelection.nEndPos;
539             }
540             getOutlinerView()->SetSelection (aSelection);
541         }
542 
543         // When not beginning the search at the beginning of the search area
544         // then there may be matches before the current position.
545         mbMatchMayExist = (maObjectIterator!=sd::outliner::OutlinerContainer(this).begin());
546     }
547     else if (bOldDirectionIsForward != mbDirectionIsForward)
548     {
549         // Requested iteration direction has changed.  Turn around the iterator.
550         maObjectIterator.Reverse();
551         if (bIsAtEnd)
552         {
553             // The iterator has pointed to end(), which after the search
554             // direction is reversed, becomes begin().
555             maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
556         }
557         else
558         {
559             // The iterator has pointed to the object one ahead/before the current
560             // one.  Now move it to the one before/ahead the current one.
561             ++maObjectIterator;
562             if (maObjectIterator != sd::outliner::OutlinerContainer(this).end())
563             {
564                 ++maObjectIterator;
565             }
566         }
567 
568         mbMatchMayExist = true;
569     }
570 
571     // Initialize the last valid position with where the search starts so
572     // that it always points to a valid position.
573     maLastValidPosition = *sd::outliner::OutlinerContainer(this).current();
574 }
575 
SearchAndReplaceAll()576 bool SdOutliner::SearchAndReplaceAll()
577 {
578     bool bRet = true;
579 
580     // Save the current position to be restored after having replaced all
581     // matches.
582     RememberStartPosition ();
583 
584     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
585     if ( ! pViewShell)
586     {
587         OSL_ASSERT(pViewShell);
588         return true;
589     }
590 
591     std::vector<sd::SearchSelection> aSelections;
592     if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
593     {
594         // Put the cursor to the beginning/end of the outliner.
595         getOutlinerView()->SetSelection (GetSearchStartPosition ());
596 
597         // The outliner does all the work for us when we are in this mode.
598         SearchAndReplaceOnce();
599     }
600     else if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
601     {
602         // Disable selection change notifications during search all.
603         SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
604         rSfxViewShell.setTiledSearching(true);
605         comphelper::ScopeGuard aGuard([&rSfxViewShell]()
606         {
607             rSfxViewShell.setTiledSearching(false);
608         });
609 
610         // Go to beginning/end of document.
611         maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
612         // Switch to the first object which contains the search string.
613         ProvideNextTextObject();
614         if( !mbStringFound  )
615         {
616             RestoreStartPosition ();
617             mnStartPageIndex = sal_uInt16(-1);
618             return true;
619         }
620         // Reset the iterator back to the beginning
621         maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
622 
623         // Search/replace until the end of the document is reached.
624         bool bFoundMatch;
625         do
626         {
627             bFoundMatch = ! SearchAndReplaceOnce(&aSelections);
628             if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && bFoundMatch && aSelections.size() == 1)
629             {
630                 // Without this, RememberStartPosition() will think it already has a remembered position.
631                 mnStartPageIndex = sal_uInt16(-1);
632 
633                 RememberStartPosition();
634 
635                 // So when RestoreStartPosition() restores the first match, then spellchecker doesn't kill the selection.
636                 bRet = false;
637             }
638         }
639         while (bFoundMatch);
640 
641         if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !aSelections.empty())
642         {
643             boost::property_tree::ptree aTree;
644             aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr());
645             aTree.put("highlightAll", true);
646 
647             boost::property_tree::ptree aChildren;
648             for (const sd::SearchSelection& rSelection : aSelections)
649             {
650                 boost::property_tree::ptree aChild;
651                 aChild.put("part", OString::number(rSelection.m_nPage).getStr());
652                 aChild.put("rectangles", rSelection.m_aRectangles.getStr());
653                 aChildren.push_back(std::make_pair("", aChild));
654             }
655             aTree.add_child("searchResultSelection", aChildren);
656 
657             std::stringstream aStream;
658             boost::property_tree::write_json(aStream, aTree);
659             OString aPayload = aStream.str().c_str();
660             rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr());
661         }
662     }
663 
664     RestoreStartPosition ();
665 
666     if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !bRet)
667     {
668         // Find-all, tiled rendering and we have at least one match.
669         OString aPayload = OString::number(mnStartPageIndex);
670         SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
671         rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
672 
673         // Emit a selection callback here:
674         // 1) The original one is no longer valid, as we there was a SET_PART in between
675         // 2) The underlying editeng will only talk about the first match till
676         // it doesn't support multi-selection.
677         std::vector<OString> aRectangles;
678         for (const sd::SearchSelection& rSelection : aSelections)
679         {
680             if (rSelection.m_nPage == mnStartPageIndex)
681                 aRectangles.push_back(rSelection.m_aRectangles);
682         }
683         OString sRectangles = comphelper::string::join("; ", aRectangles);
684         rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr());
685     }
686 
687     mnStartPageIndex = sal_uInt16(-1);
688 
689     return bRet;
690 }
691 
692 namespace
693 {
694 
getPDFSelection(const std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,const SdrObject * pObject)695 basegfx::B2DRectangle getPDFSelection(const std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,
696                                        const SdrObject* pObject)
697 {
698     basegfx::B2DRectangle aSelection;
699 
700     auto const & rTextRectangles = rVectorGraphicSearch->getTextRectangles();
701     if (rTextRectangles.empty())
702         return aSelection;
703 
704     basegfx::B2DSize aPdfPageSizeHMM = rVectorGraphicSearch->pageSize();
705 
706     basegfx::B2DRectangle aObjectB2DRectHMM(vcl::unotools::b2DRectangleFromRectangle(pObject->GetLogicRect()));
707 
708     // Setup coordinate conversion matrix to convert the inner PDF
709     // coordinates to the page relative coordinates
710     basegfx::B2DHomMatrix aB2DMatrix;
711 
712     aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSizeHMM.getX(),
713                      aObjectB2DRectHMM.getHeight() / aPdfPageSizeHMM.getY());
714 
715     aB2DMatrix.translate(aObjectB2DRectHMM.getMinX(), aObjectB2DRectHMM.getMinY());
716 
717 
718     for (auto const & rRectangle : rVectorGraphicSearch->getTextRectangles())
719     {
720         basegfx::B2DRectangle aRectangle(rRectangle);
721         aRectangle *= aB2DMatrix;
722 
723         if (aSelection.isEmpty())
724             aSelection = aRectangle;
725         else
726             aSelection.expand(aRectangle);
727     }
728 
729     return aSelection;
730 }
731 
732 } // end namespace
733 
sendLOKSearchResultCallback(const std::shared_ptr<sd::ViewShell> & pViewShell,const OutlinerView * pOutlinerView,std::vector<sd::SearchSelection> * pSelections)734 void SdOutliner::sendLOKSearchResultCallback(const std::shared_ptr<sd::ViewShell> & pViewShell,
735                                              const OutlinerView* pOutlinerView,
736                                              std::vector<sd::SearchSelection>* pSelections)
737 {
738     std::vector<::tools::Rectangle> aLogicRects;
739     auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext();
740     if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
741     {
742         basegfx::B2DRectangle aSelectionHMM = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj);
743 
744         tools::Rectangle aSelection(Point(aSelectionHMM.getMinX(), aSelectionHMM.getMinY()),
745                                     Size(aSelectionHMM.getWidth(), aSelectionHMM.getHeight()));
746         aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
747         aLogicRects.push_back(aSelection);
748     }
749     else
750     {
751         pOutlinerView->GetSelectionRectangles(aLogicRects);
752 
753         // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this
754         // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles
755         // which makes that method unusable for others
756         if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
757         {
758             for (tools::Rectangle& rRectangle : aLogicRects)
759             {
760                 rRectangle = OutputDevice::LogicToLogic(rRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
761             }
762         }
763     }
764 
765     std::vector<OString> aLogicRectStrings;
766     std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings),
767         [](const ::tools::Rectangle& rRectangle)
768     {
769         return rRectangle.toString();
770     });
771 
772     OString sRectangles = comphelper::string::join("; ", aLogicRectStrings);
773 
774     if (!pSelections)
775     {
776         // notify LibreOfficeKit about changed page
777         OString aPayload = OString::number(maCurrentPosition.mnPageIndex);
778         SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
779         rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
780 
781         // also about search result selections
782         boost::property_tree::ptree aTree;
783         aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr());
784         aTree.put("highlightAll", false);
785 
786         boost::property_tree::ptree aChildren;
787         boost::property_tree::ptree aChild;
788         aChild.put("part", OString::number(maCurrentPosition.mnPageIndex).getStr());
789         aChild.put("rectangles", sRectangles.getStr());
790         aChildren.push_back(std::make_pair("", aChild));
791         aTree.add_child("searchResultSelection", aChildren);
792 
793         std::stringstream aStream;
794         boost::property_tree::write_json(aStream, aTree);
795         aPayload = aStream.str().c_str();
796         rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr());
797 
798         if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
799         {
800             rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr());
801         }
802     }
803     else
804     {
805         sd::SearchSelection aSelection(maCurrentPosition.mnPageIndex, sRectangles);
806         bool bDuplicate = !pSelections->empty() && pSelections->back() == aSelection;
807         if (!bDuplicate)
808             pSelections->push_back(aSelection);
809     }
810 }
811 
SearchAndReplaceOnce(std::vector<sd::SearchSelection> * pSelections)812 bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelections)
813 {
814     DetectChange ();
815 
816     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
817 
818     if (!getOutlinerView() || !GetEditEngine().HasView(&getOutlinerView()->GetEditView()))
819     {
820         mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow);
821     }
822 
823     if (pViewShell)
824     {
825         mpView = pViewShell->GetView();
826         mpWindow = pViewShell->GetActiveWindow();
827         getOutlinerView()->SetWindow(mpWindow);
828         auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext();
829         if (nullptr != dynamic_cast<const sd::DrawViewShell*>(pViewShell.get()))
830         {
831             sal_uLong nMatchCount = 0;
832 
833             if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
834             {
835                 OUString const & rString = mpSearchItem->GetSearchString();
836                 bool bBackwards = mpSearchItem->GetBackward();
837 
838                 VectorGraphicSearchOptions aOptions;
839                 aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin;
840                 aOptions.mbMatchCase = mpSearchItem->GetExact();
841                 aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly();
842 
843                 bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions);
844 
845                 if (bResult)
846                 {
847                     if (bBackwards)
848                         bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous();
849                     else
850                         bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next();
851                 }
852 
853                 if (bResult)
854                 {
855                     nMatchCount = 1;
856 
857                     SdrPageView* pPageView = mpView->GetSdrPageView();
858                     mpView->UnmarkAllObj(pPageView);
859 
860                     std::vector<basegfx::B2DRectangle> aSubSelections;
861                     basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj);
862                     if (!aSubSelection.isEmpty())
863                         aSubSelections.push_back(aSubSelection);
864                     mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
865                 }
866                 else
867                 {
868                     rVectorGraphicSearchContext.reset();
869                 }
870             }
871             else
872             {
873                 // When replacing we first check if there is a selection
874                 // indicating a match.  If there is then replace it.  The
875                 // following call to StartSearchAndReplace will then search for
876                 // the next match.
877                 if (meMode == SEARCH && mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE)
878                 {
879                     if (getOutlinerView()->GetSelection().HasRange())
880                         getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
881                 }
882 
883                 // Search for the next match.
884                 if (mpSearchItem->GetCommand() != SvxSearchCmd::REPLACE_ALL)
885                 {
886                     nMatchCount = getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
887                 }
888             }
889 
890             // Go to the next text object when there have been no matches in
891             // the current object or the whole object has already been
892             // processed.
893             if (nMatchCount==0 || mpSearchItem->GetCommand()==SvxSearchCmd::REPLACE_ALL)
894             {
895                 ProvideNextTextObject ();
896 
897                 if (!mbEndOfSearch && !rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
898                 {
899                     // Remember the current position as the last one with a
900                     // text object.
901                     maLastValidPosition = maCurrentPosition;
902 
903                     // Now that the mbEndOfSearch flag guards this block the
904                     // following assertion and return should not be
905                     // necessary anymore.
906                     DBG_ASSERT(GetEditEngine().HasView(&getOutlinerView()->GetEditView() ),
907                         "SearchAndReplace without valid view!" );
908                     if ( ! GetEditEngine().HasView( &getOutlinerView()->GetEditView() ) )
909                     {
910                         mpDrawDocument->GetDocSh()->SetWaitCursor( false );
911                         return true;
912                     }
913 
914                     if (meMode == SEARCH)
915                         getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
916                 }
917             }
918         }
919         else if (nullptr != dynamic_cast<const sd::OutlineViewShell*>(pViewShell.get()))
920         {
921             mpDrawDocument->GetDocSh()->SetWaitCursor(false);
922             // The following loop is executed more than once only when a
923             // wrap around search is done.
924             while (true)
925             {
926                 int nResult = getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
927                 if (nResult == 0)
928                 {
929                     if (HandleFailedSearch ())
930                     {
931                         getOutlinerView()->SetSelection (GetSearchStartPosition ());
932                         continue;
933                     }
934                 }
935                 else
936                     mbStringFound = true;
937                 break;
938             }
939         }
940     }
941 
942     mpDrawDocument->GetDocSh()->SetWaitCursor( false );
943 
944     if (pViewShell && comphelper::LibreOfficeKit::isActive() && mbStringFound)
945     {
946         sendLOKSearchResultCallback(pViewShell, getOutlinerView(), pSelections);
947     }
948 
949     return mbEndOfSearch;
950 }
951 
952 /** Try to detect whether the document or the view (shell) has changed since
953     the last time <member>StartSearchAndReplace()</member> has been called.
954 */
DetectChange()955 void SdOutliner::DetectChange()
956 {
957     sd::outliner::IteratorPosition aPosition (maCurrentPosition);
958 
959     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
960     std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
961         std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
962 
963     // Detect whether the view has been switched from the outside.
964     if (pDrawViewShell != nullptr
965         && (aPosition.meEditMode != pDrawViewShell->GetEditMode()
966             || aPosition.mePageKind != pDrawViewShell->GetPageKind()))
967     {
968         // Either the edit mode or the page kind has changed.
969         SetStatusEventHdl(Link<EditStatus&,void>());
970 
971         SdrPageView* pPageView = mpView->GetSdrPageView();
972         if (pPageView != nullptr)
973             mpView->UnmarkAllObj (pPageView);
974         mpView->SdrEndTextEdit();
975         SetUpdateMode(false);
976         OutlinerView* pOutlinerView = getOutlinerView();
977         if (pOutlinerView != nullptr)
978             pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) );
979         if (meMode == SPELL)
980             SetPaperSize( Size(1, 1) );
981         SetText(OUString(), GetParagraph(0));
982 
983         RememberStartPosition ();
984 
985         mnPageCount = mpDrawDocument->GetSdPageCount(pDrawViewShell->GetPageKind());
986         maObjectIterator = sd::outliner::OutlinerContainer(this).current();
987     }
988 
989     // Detect change of the set of selected objects.  If their number has
990     // changed start again with the first selected object.
991     else if (DetectSelectionChange())
992     {
993         HandleChangedSelection ();
994         maObjectIterator = sd::outliner::OutlinerContainer(this).current();
995     }
996 
997     // Detect change of page count.  Restart search at first/last page in
998     // that case.
999     else if (aPosition.meEditMode == EditMode::Page
1000         && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount)
1001     {
1002         // The number of pages has changed.
1003         mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind);
1004         maObjectIterator = sd::outliner::OutlinerContainer(this).current();
1005     }
1006     else if (aPosition.meEditMode == EditMode::MasterPage
1007         && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount)
1008     {
1009         // The number of master pages has changed.
1010         mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind);
1011         maObjectIterator = sd::outliner::OutlinerContainer(this).current();
1012     }
1013 }
1014 
DetectSelectionChange()1015 bool SdOutliner::DetectSelectionChange()
1016 {
1017     bool bSelectionHasChanged = false;
1018 
1019     // If mpObj is NULL then we have not yet found our first match.
1020     // Detecting a change makes no sense.
1021     if (mpObj != nullptr)
1022     {
1023         const size_t nMarkCount = mpView ? mpView->GetMarkedObjectList().GetMarkCount() : 0;
1024         switch (nMarkCount)
1025         {
1026             case 0:
1027                 // The selection has changed when previously there have been
1028                 // selected objects.
1029                 bSelectionHasChanged = mbRestrictSearchToSelection;
1030                 break;
1031             case 1:
1032                 // Check if the only selected object is not the one that we
1033                 // had selected.
1034                 if (mpView != nullptr)
1035                 {
1036                     SdrMark* pMark = mpView->GetMarkedObjectList().GetMark(0);
1037                     if (pMark != nullptr)
1038                         bSelectionHasChanged = (mpObj != pMark->GetMarkedSdrObj ());
1039                 }
1040                 break;
1041             default:
1042                 // We had selected exactly one object.
1043                 bSelectionHasChanged = true;
1044                 break;
1045         }
1046     }
1047 
1048     return bSelectionHasChanged;
1049 }
1050 
RememberStartPosition()1051 void SdOutliner::RememberStartPosition()
1052 {
1053     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1054     if ( ! pViewShell)
1055     {
1056         OSL_ASSERT(pViewShell);
1057         return;
1058     }
1059 
1060     if ( mnStartPageIndex != sal_uInt16(-1) )
1061         return;
1062 
1063     if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
1064     {
1065         std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
1066             std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1067         if (pDrawViewShell != nullptr)
1068         {
1069             meStartViewMode = pDrawViewShell->GetPageKind();
1070             meStartEditMode = pDrawViewShell->GetEditMode();
1071             mnStartPageIndex = pDrawViewShell->GetCurPagePos();
1072         }
1073 
1074         if (mpView != nullptr)
1075         {
1076             mpStartEditedObject = mpView->GetTextEditObject();
1077             if (mpStartEditedObject != nullptr)
1078             {
1079                 // Try to retrieve current caret position only when there is an
1080                 // edited object.
1081                 ::Outliner* pOutliner =
1082                     static_cast<sd::DrawView*>(mpView)->GetTextEditOutliner();
1083                 if (pOutliner!=nullptr && pOutliner->GetViewCount()>0)
1084                 {
1085                     OutlinerView* pOutlinerView = pOutliner->GetView(0);
1086                     maStartSelection = pOutlinerView->GetSelection();
1087                 }
1088             }
1089         }
1090     }
1091     else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1092     {
1093         // Remember the current cursor position.
1094         OutlinerView* pView = GetView(0);
1095         if (pView != nullptr)
1096             pView->GetSelection();
1097     }
1098     else
1099     {
1100         mnStartPageIndex = sal_uInt16(-1);
1101     }
1102 }
1103 
RestoreStartPosition()1104 void SdOutliner::RestoreStartPosition()
1105 {
1106     bool bRestore = true;
1107     // Take a negative start page index as indicator that restoring the
1108     // start position is not requested.
1109     if (mnStartPageIndex == sal_uInt16(-1) )
1110         bRestore = false;
1111     // Don't restore when the view shell is not valid.
1112     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1113     if (pViewShell == nullptr)
1114         bRestore = false;
1115 
1116     if (!bRestore)
1117         return;
1118 
1119     if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
1120     {
1121         std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
1122             std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1123         SetViewMode (meStartViewMode);
1124         if (pDrawViewShell != nullptr)
1125         {
1126             SetPage (meStartEditMode, mnStartPageIndex);
1127             mpObj = mpStartEditedObject;
1128             if (mpObj)
1129             {
1130                 PutTextIntoOutliner();
1131                 EnterEditMode(false);
1132                 if (getOutlinerView())
1133                     getOutlinerView()->SetSelection(maStartSelection);
1134             }
1135         }
1136     }
1137     else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1138     {
1139         // Set cursor to its old position.
1140         OutlinerView* pView = GetView(0);
1141         if (pView != nullptr)
1142             pView->SetSelection (maStartSelection);
1143     }
1144 }
1145 
1146 namespace
1147 {
1148 
lclIsValidTextObject(const sd::outliner::IteratorPosition & rPosition)1149 bool lclIsValidTextObject(const sd::outliner::IteratorPosition& rPosition)
1150 {
1151     auto* pObject = dynamic_cast< SdrTextObj* >( rPosition.mxObject.get() );
1152     return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj();
1153 }
1154 
isValidVectorGraphicObject(const sd::outliner::IteratorPosition & rPosition)1155 bool isValidVectorGraphicObject(const sd::outliner::IteratorPosition& rPosition)
1156 {
1157     auto* pGraphicObject = dynamic_cast<SdrGrafObj*>(rPosition.mxObject.get());
1158     if (pGraphicObject)
1159     {
1160         auto const& pVectorGraphicData = pGraphicObject->GetGraphic().getVectorGraphicData();
1161         if (pVectorGraphicData && VectorGraphicDataType::Pdf == pVectorGraphicData->getType())
1162         {
1163             return true;
1164         }
1165     }
1166     return false;
1167 }
1168 
1169 } // end anonymous namespace
1170 
1171 
1172 /** The main purpose of this method is to iterate over all shape objects of
1173     the search area (current selection, current view, or whole document)
1174     until a text object has been found that contains at least one match or
1175     until no such object can be found anymore.   These two conditions are
1176     expressed by setting one of the flags <member>mbFoundObject</member> or
1177     <member>mbEndOfSearch</member> to <TRUE/>.
1178 */
ProvideNextTextObject()1179 void SdOutliner::ProvideNextTextObject()
1180 {
1181     mbEndOfSearch = false;
1182     mbFoundObject = false;
1183 
1184     // reset the vector search
1185     auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext();
1186     rVectorGraphicSearchContext.reset();
1187 
1188     mpView->UnmarkAllObj (mpView->GetSdrPageView());
1189     try
1190     {
1191         mpView->SdrEndTextEdit();
1192     }
1193     catch (const css::uno::Exception&)
1194     {
1195         DBG_UNHANDLED_EXCEPTION("sd.view");
1196     }
1197     SetUpdateMode(false);
1198     OutlinerView* pOutlinerView = getOutlinerView();
1199     if (pOutlinerView != nullptr)
1200         pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) );
1201     if (meMode == SPELL)
1202         SetPaperSize( Size(1, 1) );
1203     SetText(OUString(), GetParagraph(0));
1204 
1205     mpSearchSpellTextObj = nullptr;
1206 
1207     // Iterate until a valid text object has been found or the search ends.
1208     do
1209     {
1210         mpObj = nullptr;
1211         mpParaObj = nullptr;
1212 
1213         if (maObjectIterator != sd::outliner::OutlinerContainer(this).end())
1214         {
1215             maCurrentPosition = *maObjectIterator;
1216 
1217             // LOK: do not descent to notes or master pages when searching
1218             bool bForbiddenPage = comphelper::LibreOfficeKit::isActive() && (maCurrentPosition.mePageKind != PageKind::Standard || maCurrentPosition.meEditMode != EditMode::Page);
1219 
1220             rVectorGraphicSearchContext.reset();
1221 
1222             if (!bForbiddenPage)
1223             {
1224                 // Switch to the current object only if it is a valid text object.
1225                 if (lclIsValidTextObject(maCurrentPosition))
1226                 {
1227                     // Don't set yet in case of searching: the text object may not match.
1228                     if (meMode != SEARCH)
1229                         mpObj = SetObject(maCurrentPosition);
1230                     else
1231                         mpObj = maCurrentPosition.mxObject.get();
1232                 }
1233                 // Or if the object is a valid graphic object which contains vector graphic
1234                 else if (meMode == SEARCH && isValidVectorGraphicObject(maCurrentPosition))
1235                 {
1236                     mpObj = maCurrentPosition.mxObject.get();
1237                     rVectorGraphicSearchContext.mbCurrentIsVectorGraphic = true;
1238                 }
1239             }
1240 
1241             // Advance to the next object
1242             ++maObjectIterator;
1243 
1244             if (mpObj)
1245             {
1246                 if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
1247                 {
1248                     // We know here the object is a SdrGrafObj and that it
1249                     // contains a vector graphic
1250                     auto* pGraphicObject = static_cast<SdrGrafObj*>(mpObj);
1251                     OUString const & rString = mpSearchItem->GetSearchString();
1252                     bool bBackwards = mpSearchItem->GetBackward();
1253 
1254                     VectorGraphicSearchOptions aOptions;
1255                     aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin;
1256                     aOptions.mbMatchCase = mpSearchItem->GetExact();
1257                     aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly();
1258 
1259                     rVectorGraphicSearchContext.mpVectorGraphicSearch = std::make_unique<VectorGraphicSearch>(pGraphicObject->GetGraphic());
1260 
1261                     bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions);
1262                     if (bResult)
1263                     {
1264                         if (bBackwards)
1265                             bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous();
1266                         else
1267                             bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next();
1268                     }
1269 
1270                     if (bResult)
1271                     {
1272                         mpObj = SetObject(maCurrentPosition);
1273 
1274                         mbStringFound = true;
1275                         mbMatchMayExist = true;
1276                         mbFoundObject = true;
1277 
1278                         SdrPageView* pPageView = mpView->GetSdrPageView();
1279                         mpView->UnmarkAllObj(pPageView);
1280 
1281                         std::vector<basegfx::B2DRectangle> aSubSelections;
1282                         basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj);
1283                         if (!aSubSelection.isEmpty())
1284                             aSubSelections.push_back(aSubSelection);
1285 
1286                         mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
1287 
1288                         mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1289                     }
1290                     else
1291                     {
1292                         rVectorGraphicSearchContext.reset();
1293                     }
1294                 }
1295                 else
1296                 {
1297                     PutTextIntoOutliner();
1298 
1299                     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1300                     if (pViewShell != nullptr)
1301                     {
1302                         switch (meMode)
1303                         {
1304                             case SEARCH:
1305                                 PrepareSearchAndReplace ();
1306                                 break;
1307                             case SPELL:
1308                                 PrepareSpellCheck ();
1309                                 break;
1310                             case TEXT_CONVERSION:
1311                                 PrepareConversion();
1312                                 break;
1313                         }
1314                     }
1315                 }
1316             }
1317         }
1318         else
1319         {
1320             rVectorGraphicSearchContext.reset();
1321 
1322             if (meMode == SEARCH)
1323                 // Instead of doing a full-blown SetObject(), which would do the same -- but would also possibly switch pages.
1324                 mbStringFound = false;
1325 
1326             mbEndOfSearch = true;
1327             EndOfSearch ();
1328         }
1329     }
1330     while ( ! (mbFoundObject || mbEndOfSearch));
1331 }
1332 
EndOfSearch()1333 void SdOutliner::EndOfSearch()
1334 {
1335     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1336     if ( ! pViewShell)
1337     {
1338         OSL_ASSERT(pViewShell);
1339         return;
1340     }
1341 
1342     // Before we display a dialog we first jump to where the last valid text
1343     // object was found.  All page and view mode switching since then was
1344     // temporary and should not be visible to the user.
1345     if(  nullptr == dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1346         SetObject (maLastValidPosition);
1347 
1348     if (mbRestrictSearchToSelection)
1349         ShowEndOfSearchDialog ();
1350     else
1351     {
1352         // When no match has been found so far then terminate the search.
1353         if ( ! mbMatchMayExist)
1354         {
1355             ShowEndOfSearchDialog ();
1356             mbEndOfSearch = true;
1357         }
1358         // Ask the user whether to wrap around and continue the search or
1359         // to terminate.
1360         else if (meMode==TEXT_CONVERSION || ShowWrapAroundDialog ())
1361         {
1362             mbMatchMayExist = false;
1363             // Everything back to beginning (or end?) of the document.
1364             maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
1365             if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1366             {
1367                 // Set cursor to first character of the document.
1368                 OutlinerView* pOutlinerView = getOutlinerView();
1369                 if (pOutlinerView != nullptr)
1370                     pOutlinerView->SetSelection (GetSearchStartPosition ());
1371             }
1372 
1373             mbEndOfSearch = false;
1374         }
1375         else
1376         {
1377             // No wrap around.
1378             mbEndOfSearch = true;
1379         }
1380     }
1381 }
1382 
ShowEndOfSearchDialog()1383 void SdOutliner::ShowEndOfSearchDialog()
1384 {
1385     if (meMode == SEARCH)
1386     {
1387         if (!mbStringFound)
1388         {
1389             SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
1390             std::shared_ptr<sd::ViewShell> pViewShell(mpWeakViewShell.lock());
1391             if (pViewShell)
1392             {
1393                 SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
1394                 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, mpSearchItem->GetSearchString().toUtf8().getStr());
1395             }
1396         }
1397 
1398         // don't do anything else for search
1399         return;
1400     }
1401 
1402     OUString aString;
1403     if (mpView->AreObjectsMarked())
1404         aString = SdResId(STR_END_SPELLING_OBJ);
1405     else
1406         aString = SdResId(STR_END_SPELLING);
1407 
1408     // Show the message in an info box that is modal with respect to the whole application.
1409     weld::Window* pParent = GetMessageBoxParent();
1410     std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
1411                                                   VclMessageType::Info, VclButtonsType::Ok, aString));
1412     xInfoBox->run();
1413 }
1414 
ShowWrapAroundDialog()1415 bool SdOutliner::ShowWrapAroundDialog()
1416 {
1417     // Determine whether to show the dialog.
1418     if (mpSearchItem)
1419     {
1420         // When searching display the dialog only for single find&replace.
1421         const SvxSearchCmd nCommand(mpSearchItem->GetCommand());
1422         if (nCommand == SvxSearchCmd::REPLACE || nCommand == SvxSearchCmd::FIND)
1423         {
1424             if (mbDirectionIsForward)
1425                 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
1426             else
1427                 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start);
1428 
1429             return true;
1430         }
1431         else
1432             return false;
1433     }
1434 
1435     // show dialog only for spelling
1436     if (meMode != SPELL)
1437         return false;
1438 
1439     // The question text depends on the search direction.
1440     bool bImpress = mpDrawDocument && mpDrawDocument->GetDocumentType() == DocumentType::Impress;
1441 
1442     const char* pStringId;
1443     if (mbDirectionIsForward)
1444         pStringId = bImpress ? STR_SAR_WRAP_FORWARD : STR_SAR_WRAP_FORWARD_DRAW;
1445     else
1446         pStringId = bImpress ? STR_SAR_WRAP_BACKWARD : STR_SAR_WRAP_BACKWARD_DRAW;
1447 
1448     // Pop up question box that asks the user whether to wrap around.
1449     // The dialog is made modal with respect to the whole application.
1450     weld::Window* pParent = GetMessageBoxParent();
1451     std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParent,
1452                                                    VclMessageType::Question, VclButtonsType::YesNo, SdResId(pStringId)));
1453     sal_uInt16 nBoxResult = xQueryBox->run();
1454 
1455     return (nBoxResult == RET_YES);
1456 }
1457 
PutTextIntoOutliner()1458 void SdOutliner::PutTextIntoOutliner()
1459 {
1460     mpSearchSpellTextObj = dynamic_cast<SdrTextObj*>( mpObj );
1461     if ( mpSearchSpellTextObj && mpSearchSpellTextObj->HasText() && !mpSearchSpellTextObj->IsEmptyPresObj() )
1462     {
1463         SdrText* pText = mpSearchSpellTextObj->getText( maCurrentPosition.mnText );
1464         mpParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
1465 
1466         if (mpParaObj != nullptr)
1467         {
1468             SetText(*mpParaObj);
1469 
1470             ClearModifyFlag();
1471         }
1472     }
1473     else
1474     {
1475         mpSearchSpellTextObj = nullptr;
1476     }
1477 }
1478 
PrepareSpellCheck()1479 void SdOutliner::PrepareSpellCheck()
1480 {
1481     EESpellState eState = HasSpellErrors();
1482     DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker");
1483 
1484     if (eState == EESpellState::Ok)
1485         return;
1486 
1487     // When spell checking we have to test whether we have processed the
1488     // whole document and have reached the start page again.
1489     if (meMode == SPELL)
1490     {
1491         if (maSearchStartPosition == sd::outliner::Iterator())
1492             // Remember the position of the first text object so that we
1493             // know when we have processed the whole document.
1494             maSearchStartPosition = maObjectIterator;
1495         else if (maSearchStartPosition == maObjectIterator)
1496         {
1497             mbEndOfSearch = true;
1498         }
1499     }
1500 
1501     EnterEditMode( false );
1502 }
1503 
PrepareSearchAndReplace()1504 void SdOutliner::PrepareSearchAndReplace()
1505 {
1506     if (!HasText( *mpSearchItem ))
1507         return;
1508 
1509     // Set the object now that we know it matches.
1510     mpObj = SetObject(maCurrentPosition);
1511 
1512     mbStringFound = true;
1513     mbMatchMayExist = true;
1514 
1515     EnterEditMode(false);
1516 
1517     mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1518     // Start search at the right end of the current object's text
1519     // depending on the search direction.
1520     OutlinerView* pOutlinerView = getOutlinerView();
1521     if (pOutlinerView != nullptr)
1522         pOutlinerView->SetSelection (GetSearchStartPosition ());
1523 }
1524 
SetViewMode(PageKind ePageKind)1525 void SdOutliner::SetViewMode (PageKind ePageKind)
1526 {
1527     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1528     std::shared_ptr<sd::DrawViewShell> pDrawViewShell(
1529         std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1530     if (pDrawViewShell == nullptr || ePageKind == pDrawViewShell->GetPageKind())
1531         return;
1532 
1533     // Restore old edit mode.
1534     pDrawViewShell->ChangeEditMode(mpImpl->meOriginalEditMode, false);
1535 
1536     SetStatusEventHdl(Link<EditStatus&,void>());
1537     OUString sViewURL;
1538     switch (ePageKind)
1539     {
1540         case PageKind::Standard:
1541         default:
1542             sViewURL = sd::framework::FrameworkHelper::msImpressViewURL;
1543             break;
1544         case PageKind::Notes:
1545             sViewURL = sd::framework::FrameworkHelper::msNotesViewURL;
1546             break;
1547         case PageKind::Handout:
1548             sViewURL = sd::framework::FrameworkHelper::msHandoutViewURL;
1549             break;
1550     }
1551     // The text object iterator is destroyed when the shells are
1552     // switched but we need it so save it and restore it afterwards.
1553     sd::outliner::Iterator aIterator (maObjectIterator);
1554     bool bMatchMayExist = mbMatchMayExist;
1555 
1556     sd::ViewShellBase& rBase = pViewShell->GetViewShellBase();
1557 
1558     rtl::Reference<sd::FuSearch> xFuSearch;
1559     if (pViewShell->GetView())
1560         xFuSearch = pViewShell->GetView()->getSearchContext().getFunctionSearch();
1561 
1562     SetViewShell(std::shared_ptr<sd::ViewShell>());
1563     sd::framework::FrameworkHelper::Instance(rBase)->RequestView(
1564         sViewURL,
1565         sd::framework::FrameworkHelper::msCenterPaneURL);
1566 
1567     // Force (well, request) a synchronous update of the configuration.
1568     // In a better world we would handle the asynchronous view update
1569     // instead.  But that would involve major restructuring of the
1570     // Outliner code.
1571     sd::framework::FrameworkHelper::Instance(rBase)->RequestSynchronousUpdate();
1572 
1573     auto pNewViewShell = rBase.GetMainViewShell();
1574     SetViewShell(pNewViewShell);
1575     if (xFuSearch.is() && pNewViewShell->GetView())
1576         pNewViewShell->GetView()->getSearchContext().setSearchFunction(xFuSearch);
1577 
1578     // Switching to another view shell has intermediatly called
1579     // EndSpelling().  A PrepareSpelling() is pending, so call that now.
1580     PrepareSpelling();
1581 
1582     // Update the number of pages so that
1583     // <member>DetectChange()</member> has the correct value to compare
1584     // to.
1585     mnPageCount = mpDrawDocument->GetSdPageCount(ePageKind);
1586 
1587     maObjectIterator = aIterator;
1588     mbMatchMayExist = bMatchMayExist;
1589 
1590     // Save edit mode so that it can be restored when switching the view
1591     // shell again.
1592     pDrawViewShell = std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell);
1593     OSL_ASSERT(pDrawViewShell != nullptr);
1594     if (pDrawViewShell != nullptr)
1595         mpImpl->meOriginalEditMode = pDrawViewShell->GetEditMode();
1596 }
1597 
SetPage(EditMode eEditMode,sal_uInt16 nPageIndex)1598 void SdOutliner::SetPage (EditMode eEditMode, sal_uInt16 nPageIndex)
1599 {
1600     if ( ! mbRestrictSearchToSelection)
1601     {
1602         std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1603         std::shared_ptr<sd::DrawViewShell> pDrawViewShell(
1604             std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1605         OSL_ASSERT(pDrawViewShell != nullptr);
1606         if (pDrawViewShell != nullptr)
1607         {
1608             pDrawViewShell->ChangeEditMode(eEditMode, false);
1609             pDrawViewShell->SwitchPage(nPageIndex);
1610         }
1611     }
1612 }
1613 
EnterEditMode(bool bGrabFocus)1614 void SdOutliner::EnterEditMode (bool bGrabFocus)
1615 {
1616     OutlinerView* pOutlinerView = getOutlinerView();
1617     if (!(pOutlinerView && mpSearchSpellTextObj))
1618         return;
1619 
1620     pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1)));
1621     SetPaperSize( mpSearchSpellTextObj->GetLogicRect().GetSize() );
1622     SdrPageView* pPV = mpView->GetSdrPageView();
1623 
1624     // Make FuText the current function.
1625     SfxUInt16Item aItem (SID_TEXTEDIT, 1);
1626     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1627     if (!(pViewShell && pViewShell->GetDispatcher()))
1628         return;
1629 
1630     pViewShell->GetDispatcher()->ExecuteList(
1631         SID_TEXTEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, {&aItem});
1632 
1633     if (mpView->IsTextEdit())
1634     {
1635         // end text edition before starting it again
1636         mpView->SdrEndTextEdit();
1637     }
1638 
1639     // To be consistent with the usual behaviour in the Office the text
1640     // object that is put into edit mode would have also to be selected.
1641     // Starting the text edit mode is not enough so we do it here by
1642     // hand.
1643     mpView->UnmarkAllObj(pPV);
1644     mpView->MarkObj(mpSearchSpellTextObj, pPV);
1645 
1646     mpSearchSpellTextObj->setActiveText(mnText);
1647 
1648     // Turn on the edit mode for the text object.
1649     mpView->SdrBeginTextEdit(mpSearchSpellTextObj, pPV, mpWindow, true, this,
1650                             pOutlinerView, true, true, bGrabFocus);
1651 
1652     SetUpdateMode(true);
1653     mbFoundObject = true;
1654 }
1655 
GetSearchStartPosition() const1656 ESelection SdOutliner::GetSearchStartPosition() const
1657 {
1658     ESelection aPosition;
1659     if (mbDirectionIsForward)
1660     {
1661         // The default constructor uses the beginning of the text as default.
1662         aPosition = ESelection ();
1663     }
1664     else
1665     {
1666         // Retrieve the position after the last character in the last
1667         // paragraph.
1668         sal_Int32 nParagraphCount = GetParagraphCount();
1669         if (nParagraphCount == 0)
1670             aPosition = ESelection();
1671         else
1672         {
1673             sal_Int32 nLastParagraphLength = GetEditEngine().GetTextLen (
1674                 nParagraphCount-1);
1675             aPosition = ESelection (nParagraphCount-1, nLastParagraphLength);
1676         }
1677     }
1678 
1679     return aPosition;
1680 }
1681 
HasNoPreviousMatch()1682 bool SdOutliner::HasNoPreviousMatch()
1683 {
1684     OutlinerView* pOutlinerView = getOutlinerView();
1685 
1686     DBG_ASSERT (pOutlinerView!=nullptr, "outline view in SdOutliner::HasNoPreviousMatch is NULL");
1687 
1688     // Detect whether the cursor stands at the beginning
1689     // resp. at the end of the text.
1690     return pOutlinerView->GetSelection() == GetSearchStartPosition();
1691 }
1692 
HandleFailedSearch()1693 bool SdOutliner::HandleFailedSearch()
1694 {
1695     bool bContinueSearch = false;
1696 
1697     OutlinerView* pOutlinerView = getOutlinerView();
1698     if (pOutlinerView != nullptr && mpSearchItem != nullptr)
1699     {
1700         // Detect whether there is/may be a prior match.  If there is then
1701         // ask the user whether to wrap around.  Otherwise tell the user
1702         // that there is no match.
1703         if (HasNoPreviousMatch ())
1704         {
1705             // No match found in the whole presentation.
1706             SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
1707         }
1708 
1709         else
1710         {
1711             // No further matches found.  Ask the user whether to wrap
1712             // around and start again.
1713             bContinueSearch = ShowWrapAroundDialog();
1714         }
1715     }
1716 
1717     return bContinueSearch;
1718 }
1719 
SetObject(const sd::outliner::IteratorPosition & rPosition)1720 SdrObject* SdOutliner::SetObject (
1721     const sd::outliner::IteratorPosition& rPosition)
1722 {
1723     SetViewMode (rPosition.mePageKind);
1724     SetPage (rPosition.meEditMode, static_cast<sal_uInt16>(rPosition.mnPageIndex));
1725     mnText = rPosition.mnText;
1726     return rPosition.mxObject.get();
1727 }
1728 
SetViewShell(const std::shared_ptr<sd::ViewShell> & rpViewShell)1729 void SdOutliner::SetViewShell (const std::shared_ptr<sd::ViewShell>& rpViewShell)
1730 {
1731     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1732     if (pViewShell == rpViewShell)
1733         return;
1734 
1735     // Set the new view shell.
1736     mpWeakViewShell = rpViewShell;
1737     // When the outline view is not owned by us then we have to clear
1738     // that pointer so that the current one for the new view shell will
1739     // be used (in ProvideOutlinerView).
1740     if (rpViewShell)
1741     {
1742         mpView = rpViewShell->GetView();
1743 
1744         mpWindow = rpViewShell->GetActiveWindow();
1745 
1746         mpImpl->ProvideOutlinerView(*this, rpViewShell, mpWindow);
1747         OutlinerView* pOutlinerView = getOutlinerView();
1748         if (pOutlinerView != nullptr)
1749             pOutlinerView->SetWindow(mpWindow);
1750     }
1751     else
1752     {
1753         mpView = nullptr;
1754         mpWindow = nullptr;
1755     }
1756 }
1757 
HandleChangedSelection()1758 void SdOutliner::HandleChangedSelection()
1759 {
1760     maMarkListCopy.clear();
1761     mbRestrictSearchToSelection = mpView->AreObjectsMarked();
1762     if (!mbRestrictSearchToSelection)
1763         return;
1764 
1765     // Make a copy of the current mark list.
1766     const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
1767     const size_t nCount = rMarkList.GetMarkCount();
1768     if (nCount > 0)
1769     {
1770         maMarkListCopy.clear();
1771         maMarkListCopy.reserve (nCount);
1772         for (size_t i=0; i<nCount; ++i)
1773             maMarkListCopy.emplace_back(rMarkList.GetMark(i)->GetMarkedSdrObj ());
1774     }
1775     else
1776         // No marked object.  Is this case possible?
1777         mbRestrictSearchToSelection = false;
1778 }
1779 
StartConversion(LanguageType nSourceLanguage,LanguageType nTargetLanguage,const vcl::Font * pTargetFont,sal_Int32 nOptions,bool bIsInteractive)1780 void SdOutliner::StartConversion( LanguageType nSourceLanguage,  LanguageType nTargetLanguage,
1781         const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive )
1782 {
1783     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1784     bool bMultiDoc = nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() );
1785 
1786     meMode = TEXT_CONVERSION;
1787     mbDirectionIsForward = true;
1788     mpSearchItem = nullptr;
1789     mnConversionLanguage = nSourceLanguage;
1790 
1791     BeginConversion();
1792 
1793     OutlinerView* pOutlinerView = getOutlinerView();
1794     if (pOutlinerView != nullptr)
1795     {
1796         pOutlinerView->StartTextConversion(
1797             GetMessageBoxParent(),
1798             nSourceLanguage,
1799             nTargetLanguage,
1800             pTargetFont,
1801             nOptions,
1802             bIsInteractive,
1803             bMultiDoc);
1804     }
1805 
1806     EndConversion();
1807 }
1808 
1809 /** Prepare to do a text conversion on the current text object. This
1810     includes putting it into edit mode.
1811 */
PrepareConversion()1812 void SdOutliner::PrepareConversion()
1813 {
1814     SetUpdateMode(true);
1815     if( HasConvertibleTextPortion( mnConversionLanguage ) )
1816     {
1817         SetUpdateMode(false);
1818         mbStringFound = true;
1819         mbMatchMayExist = true;
1820 
1821         EnterEditMode(true);
1822 
1823         mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1824         // Start search at the right end of the current object's text
1825         // depending on the search direction.
1826     }
1827     else
1828     {
1829         SetUpdateMode(false);
1830     }
1831 }
1832 
BeginConversion()1833 void SdOutliner::BeginConversion()
1834 {
1835     SetRefDevice( SD_MOD()->GetVirtualRefDevice() );
1836 
1837     sd::ViewShellBase* pBase = getViewShellBase();
1838     if (pBase != nullptr)
1839         SetViewShell (pBase->GetMainViewShell());
1840 
1841     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1842     if (pViewShell)
1843     {
1844         mbStringFound = false;
1845 
1846         // Supposed that we are not located at the very beginning/end of the
1847         // document then there may be a match in the document prior/after
1848         // the current position.
1849         mbMatchMayExist = true;
1850 
1851         maObjectIterator = sd::outliner::Iterator();
1852         maSearchStartPosition = sd::outliner::Iterator();
1853         RememberStartPosition();
1854 
1855         mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow);
1856 
1857         HandleChangedSelection ();
1858     }
1859     ClearModifyFlag();
1860 }
1861 
EndConversion()1862 void SdOutliner::EndConversion()
1863 {
1864     EndSpelling();
1865 }
1866 
ConvertNextDocument()1867 bool SdOutliner::ConvertNextDocument()
1868 {
1869     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1870     if (dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ) )
1871         return false;
1872 
1873     mpDrawDocument->GetDocSh()->SetWaitCursor( true );
1874 
1875     Initialize ( true );
1876 
1877     OutlinerView* pOutlinerView = getOutlinerView();
1878     if (pOutlinerView != nullptr)
1879     {
1880         mpWindow = pViewShell->GetActiveWindow();
1881         pOutlinerView->SetWindow(mpWindow);
1882     }
1883     ProvideNextTextObject ();
1884 
1885     mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1886     ClearModifyFlag();
1887 
1888     // for text conversion we automatically wrap around one
1889     // time and stop at the start shape
1890     if( mpFirstObj )
1891     {
1892         if( (mnText == 0) && (mpFirstObj == mpObj) )
1893             return false;
1894     }
1895     else
1896     {
1897         mpFirstObj = mpObj;
1898     }
1899 
1900     return !mbEndOfSearch;
1901 }
1902 
GetMessageBoxParent()1903 weld::Window* SdOutliner::GetMessageBoxParent()
1904 {
1905     // We assume that the parent of the given message box is NULL, i.e. it is
1906     // modal with respect to the top application window. However, this
1907     // does not affect the search dialog. Therefore we have to lock it here
1908     // while the message box is being shown. We also have to take into
1909     // account that we are called during a spell check and the search dialog
1910     // is not available.
1911     weld::Window* pSearchDialog = nullptr;
1912     SfxChildWindow* pChildWindow = nullptr;
1913     switch (meMode)
1914     {
1915         case SEARCH:
1916             pChildWindow = SfxViewFrame::Current()->GetChildWindow(
1917                 SvxSearchDialogWrapper::GetChildWindowId());
1918             break;
1919 
1920         case SPELL:
1921             pChildWindow = SfxViewFrame::Current()->GetChildWindow(
1922                 sd::SpellDialogChildWindow::GetChildWindowId());
1923             break;
1924 
1925         case TEXT_CONVERSION:
1926             // There should no messages boxes be displayed while doing the
1927             // hangul hanja conversion.
1928             break;
1929     }
1930 
1931     if (pChildWindow != nullptr)
1932     {
1933         auto xController = pChildWindow->GetController();
1934         pSearchDialog = xController ? xController->getDialog() : nullptr;
1935     }
1936 
1937     if (pSearchDialog)
1938         return pSearchDialog;
1939 
1940     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1941     auto pWin = pViewShell->GetActiveWindow();
1942     return pWin ? pWin->GetFrameWeld() : nullptr;
1943 }
1944 
1945 //===== SdOutliner::Implementation ==============================================
1946 
Implementation()1947 SdOutliner::Implementation::Implementation()
1948     : meOriginalEditMode(EditMode::Page),
1949       mbOwnOutlineView(false),
1950       mpOutlineView(nullptr)
1951 {
1952 }
1953 
~Implementation()1954 SdOutliner::Implementation::~Implementation()
1955 {
1956     if (mbOwnOutlineView && mpOutlineView!=nullptr)
1957     {
1958         mpOutlineView->SetWindow(nullptr);
1959         delete mpOutlineView;
1960         mpOutlineView = nullptr;
1961     }
1962 }
1963 
1964 /** We try to create a new OutlinerView only when there is none available,
1965     either from an OutlinerViewShell or a previous call to
1966     ProvideOutlinerView().  This is necessary to support the spell checker
1967     which can not cope with exchanging the OutlinerView.
1968 */
ProvideOutlinerView(Outliner & rOutliner,const std::shared_ptr<sd::ViewShell> & rpViewShell,vcl::Window * pWindow)1969 void SdOutliner::Implementation::ProvideOutlinerView (
1970     Outliner& rOutliner,
1971     const std::shared_ptr<sd::ViewShell>& rpViewShell,
1972     vcl::Window* pWindow)
1973 {
1974     if (rpViewShell == nullptr)
1975         return;
1976 
1977     switch (rpViewShell->GetShellType())
1978     {
1979         case sd::ViewShell::ST_DRAW:
1980         case sd::ViewShell::ST_IMPRESS:
1981         case sd::ViewShell::ST_NOTES:
1982         case sd::ViewShell::ST_HANDOUT:
1983         {
1984             // Create a new outline view to do the search on.
1985             bool bInsert = false;
1986             if (mpOutlineView != nullptr && !mbOwnOutlineView)
1987                 mpOutlineView = nullptr;
1988 
1989             if (mpOutlineView == nullptr || !rOutliner.GetEditEngine().HasView(&mpOutlineView->GetEditView()))
1990             {
1991                 delete mpOutlineView;
1992                 mpOutlineView = new OutlinerView(&rOutliner, pWindow);
1993                 mbOwnOutlineView = true;
1994                 bInsert = true;
1995             }
1996             else
1997                 mpOutlineView->SetWindow(pWindow);
1998 
1999             EVControlBits nStat = mpOutlineView->GetControlWord();
2000             nStat &= ~EVControlBits::AUTOSCROLL;
2001             mpOutlineView->SetControlWord(nStat);
2002 
2003             if (bInsert)
2004                 rOutliner.InsertView( mpOutlineView );
2005 
2006             rOutliner.SetUpdateMode(false);
2007             mpOutlineView->SetOutputArea (::tools::Rectangle (Point(), Size(1, 1)));
2008             rOutliner.SetPaperSize( Size(1, 1) );
2009             rOutliner.SetText(OUString(), rOutliner.GetParagraph(0));
2010 
2011             meOriginalEditMode =
2012                 std::static_pointer_cast<sd::DrawViewShell>(rpViewShell)->GetEditMode();
2013         }
2014         break;
2015 
2016         case sd::ViewShell::ST_OUTLINE:
2017         {
2018             if (mpOutlineView!=nullptr && mbOwnOutlineView)
2019                 delete mpOutlineView;
2020             mpOutlineView = rOutliner.GetView(0);
2021             mbOwnOutlineView = false;
2022         }
2023         break;
2024 
2025         default:
2026         case sd::ViewShell::ST_NONE:
2027         case sd::ViewShell::ST_PRESENTATION:
2028             // Ignored
2029             break;
2030     }
2031 }
2032 
ReleaseOutlinerView()2033 void SdOutliner::Implementation::ReleaseOutlinerView()
2034 {
2035     if (mbOwnOutlineView)
2036     {
2037         OutlinerView* pView = mpOutlineView;
2038         mpOutlineView = nullptr;
2039         mbOwnOutlineView = false;
2040         if (pView != nullptr)
2041         {
2042             pView->SetWindow(nullptr);
2043             delete pView;
2044         }
2045     }
2046     else
2047     {
2048         mpOutlineView = nullptr;
2049     }
2050 }
2051 
2052 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2053