cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / view / Outliner.cxx
blob6f37edc79e70e2dceffca1218d623729ec983631
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 <Outliner.hxx>
21 #include <boost/property_tree/json_parser.hpp>
22 #include <vcl/settings.hxx>
23 #include <vcl/svapp.hxx>
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 <comphelper/diagnose_ex.hxx>
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 <NotesPanelView.hxx>
53 #include <drawdoc.hxx>
54 #include <DrawDocShell.hxx>
55 #include <drawview.hxx>
56 #include <ViewShellBase.hxx>
57 #include <ViewShellManager.hxx>
58 #include <SpellDialogChildWindow.hxx>
59 #include <framework/FrameworkHelper.hxx>
60 #include <svx/svxids.hrc>
61 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
62 #include <comphelper/string.hxx>
63 #include <comphelper/lok.hxx>
64 #include <comphelper/scopeguard.hxx>
65 #include <VectorGraphicSearchContext.hxx>
66 #include <fusearch.hxx>
67 #include <sdpage.hxx>
69 using namespace ::com::sun::star;
70 using namespace ::com::sun::star::uno;
71 using namespace ::com::sun::star::lang;
72 using namespace ::com::sun::star::linguistic2;
74 class SfxStyleSheetPool;
76 class SdOutliner::Implementation
78 public:
79 /** The original edit mode directly after switching to a different view
80 mode. Used for restoring the edit mode when leaving that view mode
81 again.
83 EditMode meOriginalEditMode;
85 Implementation();
86 ~Implementation();
88 /** Return the OutlinerView that was provided by the last call to
89 ProvideOutlinerView() (or NULL when there was no such call.)
91 OutlinerView* GetOutlinerView() { return mpOutlineView;}
93 /** Provide in the member mpOutlineView an instance of OutlinerView that
94 is either taken from the ViewShell, when it is an OutlineViewShell,
95 or is created. When an OutlinerView already exists it is initialized.
97 void ProvideOutlinerView (
98 Outliner& rOutliner,
99 const std::shared_ptr<sd::ViewShell>& rpViewShell,
100 vcl::Window* pWindow);
102 /** This method is called when the OutlinerView is no longer used.
104 void ReleaseOutlinerView();
106 sd::VectorGraphicSearchContext& getVectorGraphicSearchContext() { return maVectorGraphicSearchContext; }
108 private:
109 /** Flag that specifies whether we own the outline view pointed to by
110 <member>mpOutlineView</member> and thus have to
111 delete it in <member>EndSpelling()</member>.
113 bool mbOwnOutlineView;
115 /** The outline view used for searching and spelling. If searching or
116 spell checking an outline view this data member points to that view.
117 For all other views an instance is created. The
118 <member>mbOwnOutlineView</member> distinguishes between both cases.
120 OutlinerView* mpOutlineView;
122 sd::VectorGraphicSearchContext maVectorGraphicSearchContext;
125 namespace
128 sd::ViewShellBase* getViewShellBase()
130 return dynamic_cast<sd::ViewShellBase*>(SfxViewShell::Current());
133 OutlinerView* lclGetNotesPaneOutliner(const std::shared_ptr<sd::ViewShell>& pViewShell)
135 if (!pViewShell)
136 return nullptr;
138 // request the notes pane
139 sd::ViewShellBase& rBase = pViewShell->GetViewShellBase();
141 sd::framework::FrameworkHelper::Instance(rBase)->RequestView(
142 sd::framework::FrameworkHelper::msNotesPanelViewURL,
143 sd::framework::FrameworkHelper::msBottomImpressPaneURL);
145 auto pInstance = sd::framework::FrameworkHelper::Instance(rBase);
146 pInstance->RequestSynchronousUpdate();
148 std::shared_ptr<sd::ViewShell> pNotesPaneShell(
149 pInstance->GetViewShell(sd::framework::FrameworkHelper::msBottomImpressPaneURL));
151 if (!pNotesPaneShell)
152 return nullptr;
154 return static_cast<sd::NotesPanelView*>(pNotesPaneShell->GetView())->GetOutlinerView();
157 } // end anonymous namespace
159 SdOutliner::SdOutliner( SdDrawDocument* pDoc, OutlinerMode nMode )
160 : SdrOutliner( &pDoc->GetItemPool(), nMode ),
161 mpImpl(new Implementation()),
162 meMode(SEARCH),
163 mpView(nullptr),
164 mpWindow(nullptr),
165 mpDrawDocument(pDoc),
166 mnConversionLanguage(LANGUAGE_NONE),
167 mnIgnoreCurrentPageChangesLevel(0),
168 mbStringFound(false),
169 mbMatchMayExist(false),
170 mnPageCount(0),
171 mbEndOfSearch(false),
172 mbFoundObject(false),
173 mbDirectionIsForward(true),
174 mbRestrictSearchToSelection(false),
175 mpObj(nullptr),
176 mpFirstObj(nullptr),
177 mpSearchSpellTextObj(nullptr),
178 mnText(0),
179 mpParaObj(nullptr),
180 meStartViewMode(PageKind::Standard),
181 meStartEditMode(EditMode::Page),
182 mnStartPageIndex(sal_uInt16(-1)),
183 mpStartEditedObject(nullptr),
184 mbPrepareSpellingPending(true)
186 SetStyleSheetPool(static_cast<SfxStyleSheetPool*>( mpDrawDocument->GetStyleSheetPool() ));
187 SetEditTextObjectPool( &pDoc->GetItemPool() );
188 SetCalcFieldValueHdl(LINK(SdModule::get(), SdModule, CalcFieldValueHdl));
189 SetForbiddenCharsTable( pDoc->GetForbiddenCharsTable() );
191 EEControlBits nCntrl = GetControlWord();
192 nCntrl |= EEControlBits::ALLOWBIGOBJS;
193 nCntrl |= EEControlBits::MARKFIELDS;
194 nCntrl |= EEControlBits::AUTOCORRECT;
196 bool bOnlineSpell = false;
198 sd::DrawDocShell* pDocSh = mpDrawDocument->GetDocSh();
200 if (pDocSh)
202 bOnlineSpell = mpDrawDocument->GetOnlineSpell();
204 else
206 bOnlineSpell = false;
210 const SvtLinguConfig aLinguConfig;
211 Any aAny = aLinguConfig.GetProperty( UPN_IS_SPELL_AUTO );
212 aAny >>= bOnlineSpell;
214 catch( ... )
216 OSL_FAIL( "Ill. type in linguistic property" );
220 if (bOnlineSpell)
221 nCntrl |= EEControlBits::ONLINESPELLING;
222 else
223 nCntrl &= ~EEControlBits::ONLINESPELLING;
225 SetControlWord(nCntrl);
227 Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() );
228 if ( xSpellChecker.is() )
229 SetSpeller( xSpellChecker );
231 Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() );
232 if( xHyphenator.is() )
233 SetHyphenator( xHyphenator );
235 SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
238 /// Nothing spectacular in the destructor.
239 SdOutliner::~SdOutliner()
243 OutlinerView* SdOutliner::getOutlinerView()
245 return mpImpl->GetOutlinerView();
248 /** Prepare find&replace or spellchecking. This distinguishes between three
249 cases:
250 <ol>
251 <li>The current shell is a <type>DrawViewShell</type>: Create a
252 <type>OutlinerView</type> object and search all objects of (i) the
253 current mark list, (ii) of the current view, or (iii) of all the view
254 combinations:
255 <ol>
256 <li>Draw view, slide view</li>
257 <li>Draw view, background view</li>
258 <li>Notes view, slide view</li>
259 <li>Notes view, background view</li>
260 <li>Handout view, slide view</li>
261 <li>Handout view, background view</li>
262 </ol>
264 <li>When the current shell is a <type>SdOutlineViewShell</type> then
265 directly operate on it. No switching into other views takes place.</li>
266 </ol>
268 void SdOutliner::PrepareSpelling()
270 mbPrepareSpellingPending = false;
272 sd::ViewShellBase* pBase = getViewShellBase();
273 if (pBase != nullptr)
274 SetViewShell (pBase->GetMainViewShell());
275 SetRefDevice(SdModule::get()->GetVirtualRefDevice());
277 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
278 if (pViewShell)
280 mbStringFound = false;
282 // Supposed that we are not located at the very beginning/end of
283 // the document then there may be a match in the document
284 // prior/after the current position.
285 mbMatchMayExist = true;
287 maObjectIterator = sd::outliner::Iterator();
288 maSearchStartPosition = sd::outliner::Iterator();
289 RememberStartPosition();
291 mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow);
293 HandleChangedSelection ();
295 ClearModifyFlag();
298 void SdOutliner::StartSpelling()
300 meMode = SPELL;
301 mbDirectionIsForward = true;
302 mpSearchItem.reset();
305 /** Free all resources acquired during the search/spell check. After a
306 spell check the start position is restored here.
308 void SdOutliner::EndSpelling()
310 // Keep old view shell alive until we release the outliner view.
311 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
312 std::shared_ptr<sd::ViewShell> pOldViewShell (pViewShell);
314 sd::ViewShellBase* pBase = getViewShellBase();
315 if (pBase != nullptr)
316 pViewShell = pBase->GetMainViewShell();
317 else
318 pViewShell.reset();
319 mpWeakViewShell = pViewShell;
321 // When in <member>PrepareSpelling()</member> a new outline view has
322 // been created then delete it here.
323 bool bViewIsDrawViewShell(dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ));
324 if (bViewIsDrawViewShell)
326 SetStatusEventHdl(Link<EditStatus&,void>());
327 mpView = pViewShell->GetView();
328 mpView->UnmarkAllObj (mpView->GetSdrPageView());
329 mpView->SdrEndTextEdit();
330 // Make FuSelection the current function.
331 pViewShell->GetDispatcher()->Execute(
332 SID_OBJECT_SELECT,
333 SfxCallMode::SYNCHRON | SfxCallMode::RECORD);
335 // Remove and, if previously created by us, delete the outline
336 // view.
337 OutlinerView* pOutlinerView = getOutlinerView();
338 if (pOutlinerView != nullptr)
340 RemoveView(pOutlinerView);
341 mpImpl->ReleaseOutlinerView();
344 SetUpdateLayout(true);
347 // Before clearing the modify flag use it as a hint that
348 // changes were done at SpellCheck
349 if(IsModified())
351 if(auto pOutlineView = dynamic_cast<sd::OutlineView *>( mpView ))
352 pOutlineView->PrepareClose();
353 if(mpDrawDocument && !mpDrawDocument->IsChanged())
354 mpDrawDocument->SetChanged();
357 // Now clear the modify flag to have a specified state of
358 // Outliner
359 ClearModifyFlag();
361 // When spell checking then restore the start position.
362 if (meMode==SPELL || meMode==TEXT_CONVERSION)
363 RestoreStartPosition ();
365 mpWeakViewShell.reset();
366 mpView = nullptr;
367 mpWindow = nullptr;
368 mnStartPageIndex = sal_uInt16(-1);
371 bool SdOutliner::SpellNextDocument()
373 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
374 if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
376 // When doing a spell check in the outline view then there is
377 // only one document.
378 mbEndOfSearch = true;
379 EndOfSearch ();
381 else
383 if( auto pOutlineView = dynamic_cast<sd::OutlineView *>( mpView ))
384 pOutlineView->PrepareClose();
385 mpDrawDocument->GetDocSh()->SetWaitCursor( true );
387 Initialize (true);
389 mpWindow = pViewShell->GetActiveWindow();
390 OutlinerView* pOutlinerView = getOutlinerView();
391 if (pOutlinerView != nullptr)
392 pOutlinerView->SetWindow(mpWindow);
393 ProvideNextTextObject ();
395 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
396 ClearModifyFlag();
399 return !mbEndOfSearch;
403 * check next text object
405 svx::SpellPortions SdOutliner::GetNextSpellSentence()
407 svx::SpellPortions aResult;
409 DetectChange();
410 // Iterate over sentences and text shapes until a sentence with a
411 // spelling error has been found. If no such sentence can be
412 // found the loop is left through a break.
413 // It is the responsibility of the sd outliner object to correctly
414 // iterate over all text shapes, i.e. switch between views, wrap
415 // around at the end of the document, stop when all text shapes
416 // have been examined exactly once.
417 bool bFoundNextSentence = false;
418 while ( ! bFoundNextSentence)
420 OutlinerView* pOutlinerView = GetView(0);
421 if (pOutlinerView != nullptr)
423 ESelection aCurrentSelection (pOutlinerView->GetSelection());
424 if ( ! mbMatchMayExist
425 && maStartSelection < aCurrentSelection)
426 EndOfSearch();
428 // Advance to the next sentence.
429 bFoundNextSentence = SpellSentence( pOutlinerView->GetEditView(), aResult);
432 // When no sentence with spelling errors has been found in the
433 // currently selected text shape or there is no selected text
434 // shape then advance to the next text shape.
435 if ( ! bFoundNextSentence)
436 if ( ! SpellNextDocument())
437 // All text objects have been processed so exit the
438 // loop and return an empty portions list.
439 break;
442 return aResult;
445 /** Go to next match.
447 bool SdOutliner::StartSearchAndReplace (const SvxSearchItem* pSearchItem)
449 bool bEndOfSearch = true;
451 // clear the search toolbar entry
452 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
454 mpDrawDocument->GetDocSh()->SetWaitCursor( true );
456 // Since REPLACE is really a replaceAndSearchNext instead of a searchAndReplace,
457 // make sure that the search portion has not changed since the last FIND.
458 if (!mbPrepareSpellingPending && mpSearchItem
459 && pSearchItem->GetCommand() == SvxSearchCmd::REPLACE
460 && !mpSearchItem->equalsIgnoring(*pSearchItem, /*bIgnoreReplace=*/true,
461 /*bIgnoreCommand=*/true))
463 EndSpelling();
464 mbPrepareSpellingPending = true;
467 if (mbPrepareSpellingPending)
468 PrepareSpelling();
469 sd::ViewShellBase* pBase = getViewShellBase();
470 // Determine whether we have to abort the search. This is necessary
471 // when the main view shell does not support searching.
472 bool bAbort = false;
473 if (pBase != nullptr)
475 std::shared_ptr<sd::ViewShell> pShell (pBase->GetMainViewShell());
476 SetViewShell(pShell);
477 if (pShell == nullptr)
478 bAbort = true;
479 else
480 switch (pShell->GetShellType())
482 case sd::ViewShell::ST_DRAW:
483 case sd::ViewShell::ST_IMPRESS:
484 case sd::ViewShell::ST_NOTES:
485 case sd::ViewShell::ST_HANDOUT:
486 case sd::ViewShell::ST_OUTLINE:
487 bAbort = false;
488 break;
489 default:
490 bAbort = true;
491 break;
495 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
496 if ( ! pViewShell)
498 OSL_ASSERT(pViewShell);
499 return true;
502 if ( ! bAbort)
504 meMode = SEARCH;
505 mpSearchItem.reset(pSearchItem->Clone());
507 mbFoundObject = false;
509 Initialize ( ! mpSearchItem->GetBackward());
511 const SvxSearchCmd nCommand (mpSearchItem->GetCommand());
512 if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
514 bEndOfSearch = SearchAndReplaceAll ();
516 else
518 RememberStartPosition ();
519 bEndOfSearch = SearchAndReplaceOnce ();
520 // restore start position if nothing was found
521 if(!mbStringFound)
523 RestoreStartPosition ();
524 // Nothing was changed, no need to restart the spellchecker.
525 if (nCommand == SvxSearchCmd::FIND)
526 bEndOfSearch = false;
528 mnStartPageIndex = sal_uInt16(-1);
532 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
534 return bEndOfSearch;
537 void SdOutliner::Initialize (bool bDirectionIsForward)
539 const bool bIsAtEnd (maObjectIterator == sd::outliner::OutlinerContainer(this).end());
540 const bool bOldDirectionIsForward = mbDirectionIsForward;
541 mbDirectionIsForward = bDirectionIsForward;
543 if (maObjectIterator == sd::outliner::Iterator())
545 // Initialize a new search.
546 maObjectIterator = sd::outliner::OutlinerContainer(this).current();
547 maCurrentPosition = *maObjectIterator;
549 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
550 if ( ! pViewShell)
552 OSL_ASSERT(pViewShell);
553 return;
556 // In case we are searching in an outline view then first remove the
557 // current selection and place cursor at its start or end.
558 if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
560 ESelection aSelection = getOutlinerView()->GetSelection ();
561 if (mbDirectionIsForward)
563 aSelection.CollapseToStart();
565 else
567 aSelection.CollapseToEnd();
569 getOutlinerView()->SetSelection (aSelection);
572 // When not beginning the search at the beginning of the search area
573 // then there may be matches before the current position.
574 mbMatchMayExist = (maObjectIterator!=sd::outliner::OutlinerContainer(this).begin());
576 else if (bOldDirectionIsForward != mbDirectionIsForward)
578 // Requested iteration direction has changed. Turn around the iterator.
579 maObjectIterator.Reverse();
580 if (bIsAtEnd)
582 // The iterator has pointed to end(), which after the search
583 // direction is reversed, becomes begin().
584 maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
586 else
588 // The iterator has pointed to the object one ahead/before the current
589 // one. Now move it to the one before/ahead the current one.
590 ++maObjectIterator;
591 if (maObjectIterator != sd::outliner::OutlinerContainer(this).end())
593 ++maObjectIterator;
597 mbMatchMayExist = true;
600 // Initialize the last valid position with where the search starts so
601 // that it always points to a valid position.
602 maLastValidPosition = *sd::outliner::OutlinerContainer(this).current();
605 bool SdOutliner::SearchAndReplaceAll()
607 bool bRet = true;
609 // Save the current position to be restored after having replaced all
610 // matches.
611 RememberStartPosition ();
613 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
614 if ( ! pViewShell)
616 OSL_ASSERT(pViewShell);
617 return true;
620 std::vector<sd::SearchSelection> aSelections;
621 if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
623 // Put the cursor to the beginning/end of the outliner.
624 getOutlinerView()->SetSelection (GetSearchStartPosition ());
626 // The outliner does all the work for us when we are in this mode.
627 SearchAndReplaceOnce();
629 else if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
631 // Disable selection change notifications during search all.
632 SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
633 rSfxViewShell.setTiledSearching(true);
634 comphelper::ScopeGuard aGuard([&rSfxViewShell]()
636 rSfxViewShell.setTiledSearching(false);
639 // Go to beginning/end of document.
640 maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
641 // Switch to the first object which contains the search string.
642 ProvideNextTextObject();
643 if( !mbStringFound )
645 RestoreStartPosition ();
646 mnStartPageIndex = sal_uInt16(-1);
647 return true;
649 // Reset the iterator back to the beginning
650 maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
652 // Search/replace until the end of the document is reached.
653 bool bFoundMatch;
656 bFoundMatch = ! SearchAndReplaceOnce(&aSelections);
657 if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && bFoundMatch && aSelections.size() == 1)
659 // Without this, RememberStartPosition() will think it already has a remembered position.
660 mnStartPageIndex = sal_uInt16(-1);
662 RememberStartPosition();
664 // So when RestoreStartPosition() restores the first match, then spellchecker doesn't kill the selection.
665 bRet = false;
668 while (bFoundMatch);
670 if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !aSelections.empty())
672 boost::property_tree::ptree aTree;
673 aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr());
674 aTree.put("highlightAll", true);
676 boost::property_tree::ptree aChildren;
677 for (const sd::SearchSelection& rSelection : aSelections)
679 boost::property_tree::ptree aChild;
680 aChild.put("part", OString::number(rSelection.m_nPage).getStr());
681 aChild.put("rectangles", rSelection.m_aRectangles.getStr());
682 aChildren.push_back(std::make_pair("", aChild));
684 aTree.add_child("searchResultSelection", aChildren);
686 std::stringstream aStream;
687 boost::property_tree::write_json(aStream, aTree);
688 OString aPayload( aStream.str() );
689 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload);
693 RestoreStartPosition ();
695 if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !bRet)
697 // Find-all, tiled rendering and we have at least one match.
698 OString aPayload = OString::number(mnStartPageIndex);
699 SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
700 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload);
702 // Emit a selection callback here:
703 // 1) The original one is no longer valid, as we there was a SET_PART in between
704 // 2) The underlying editeng will only talk about the first match till
705 // it doesn't support multi-selection.
706 std::vector<OString> aRectangles;
707 for (const sd::SearchSelection& rSelection : aSelections)
709 if (rSelection.m_nPage == mnStartPageIndex)
710 aRectangles.push_back(rSelection.m_aRectangles);
712 OString sRectangles = comphelper::string::join("; ", aRectangles);
713 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles);
716 mnStartPageIndex = sal_uInt16(-1);
718 return bRet;
721 namespace
724 basegfx::B2DRectangle getPDFSelection(const std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,
725 const SdrObject* pObject)
727 basegfx::B2DRectangle aSelection;
729 auto const aTextRectangles = rVectorGraphicSearch->getTextRectangles();
730 if (aTextRectangles.empty())
731 return aSelection;
733 basegfx::B2DSize aPdfPageSizeHMM = rVectorGraphicSearch->pageSize();
735 basegfx::B2DRectangle aObjectB2DRectHMM(vcl::unotools::b2DRectangleFromRectangle(pObject->GetLogicRect()));
737 // Setup coordinate conversion matrix to convert the inner PDF
738 // coordinates to the page relative coordinates
739 basegfx::B2DHomMatrix aB2DMatrix;
741 aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSizeHMM.getWidth(),
742 aObjectB2DRectHMM.getHeight() / aPdfPageSizeHMM.getHeight());
744 aB2DMatrix.translate(aObjectB2DRectHMM.getMinX(), aObjectB2DRectHMM.getMinY());
747 for (auto const & rRectangle : rVectorGraphicSearch->getTextRectangles())
749 basegfx::B2DRectangle aRectangle(rRectangle);
750 aRectangle *= aB2DMatrix;
752 if (aSelection.isEmpty())
753 aSelection = aRectangle;
754 else
755 aSelection.expand(aRectangle);
758 return aSelection;
761 } // end namespace
763 void SdOutliner::sendLOKSearchResultCallback(const std::shared_ptr<sd::ViewShell> & pViewShell,
764 const OutlinerView* pOutlinerView,
765 std::vector<sd::SearchSelection>* pSelections)
767 std::vector<::tools::Rectangle> aLogicRects;
768 auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext();
769 if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
771 basegfx::B2DRectangle aSelectionHMM = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj);
773 tools::Rectangle aSelection(Point(aSelectionHMM.getMinX(), aSelectionHMM.getMinY()),
774 Size(aSelectionHMM.getWidth(), aSelectionHMM.getHeight()));
775 aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
776 aLogicRects.push_back(aSelection);
778 else
780 pOutlinerView->GetSelectionRectangles(aLogicRects);
782 // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this
783 // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles
784 // which makes that method unusable for others
785 if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
787 for (tools::Rectangle& rRectangle : aLogicRects)
789 rRectangle = o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip);
794 std::vector<OString> aLogicRectStrings;
795 std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings),
796 [](const ::tools::Rectangle& rRectangle)
798 return rRectangle.toString();
801 OString sRectangles = comphelper::string::join("; ", aLogicRectStrings);
803 if (!pSelections)
805 // notify LibreOfficeKit about changed page
806 OString aPayload = OString::number(maCurrentPosition.mnPageIndex);
807 SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
808 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload);
810 // also about search result selections
811 boost::property_tree::ptree aTree;
812 aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr());
813 aTree.put("highlightAll", false);
815 boost::property_tree::ptree aChildren;
816 boost::property_tree::ptree aChild;
817 aChild.put("part", OString::number(maCurrentPosition.mnPageIndex).getStr());
818 aChild.put("rectangles", sRectangles.getStr());
819 aChildren.push_back(std::make_pair("", aChild));
820 aTree.add_child("searchResultSelection", aChildren);
822 std::stringstream aStream;
823 boost::property_tree::write_json(aStream, aTree);
824 aPayload = OString(aStream.str());
825 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload);
827 if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
829 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles);
832 else
834 sd::SearchSelection aSelection(maCurrentPosition.mnPageIndex, sRectangles);
835 bool bDuplicate = !pSelections->empty() && pSelections->back() == aSelection;
836 if (!bDuplicate)
837 pSelections->push_back(aSelection);
841 bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelections)
843 DetectChange ();
845 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
847 if (!getOutlinerView() || !GetEditEngine().HasView(&getOutlinerView()->GetEditView()))
849 std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
850 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
852 // Perhaps the user switched to a different page/slide between searches.
853 // If so, reset the starting search position to the current slide like DetectChange does
854 if (pDrawViewShell && pDrawViewShell->GetCurPagePos() != maCurrentPosition.mnPageIndex)
855 maObjectIterator = sd::outliner::OutlinerContainer(this).current();
857 mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow);
860 if (pViewShell)
862 mpView = pViewShell->GetView();
863 mpWindow = pViewShell->GetActiveWindow();
864 getOutlinerView()->SetWindow(mpWindow);
865 auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext();
866 if (nullptr != dynamic_cast<const sd::DrawViewShell*>(pViewShell.get()))
868 sal_uLong nMatchCount = 0;
870 if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
872 OUString const & rString = mpSearchItem->GetSearchString();
873 bool bBackwards = mpSearchItem->GetBackward();
875 VectorGraphicSearchOptions aOptions;
876 aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin;
877 aOptions.mbMatchCase = mpSearchItem->GetExact();
878 aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly();
880 bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions);
882 if (bResult)
884 if (bBackwards)
885 bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous();
886 else
887 bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next();
890 if (bResult)
892 nMatchCount = 1;
894 SdrPageView* pPageView = mpView->GetSdrPageView();
895 mpView->UnmarkAllObj(pPageView);
897 std::vector<basegfx::B2DRectangle> aSubSelections;
898 basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj);
899 if (!aSubSelection.isEmpty())
900 aSubSelections.push_back(aSubSelection);
901 mpView->MarkObj(mpObj, pPageView, false, false, std::move(aSubSelections));
903 else
905 rVectorGraphicSearchContext.reset();
908 else
910 // When replacing we first check if there is a selection
911 // indicating a match. If there is then replace it. The
912 // following call to StartSearchAndReplace will then search for
913 // the next match.
914 if (meMode == SEARCH && mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE)
916 if (getOutlinerView()->GetSelection().HasRange())
917 getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
920 // Search for the next match.
921 if (mpSearchItem->GetCommand() != SvxSearchCmd::REPLACE_ALL)
923 nMatchCount = getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
924 if (nMatchCount && maCurrentPosition.meEditMode == EditMode::Page
925 && maCurrentPosition.mePageKind == PageKind::Notes)
927 if(auto pNotesPaneOutliner = lclGetNotesPaneOutliner(pViewShell))
929 pNotesPaneOutliner->SetSelection(getOutlinerView()->GetSelection());
935 // Go to the next text object when there have been no matches in
936 // the current object or the whole object has already been
937 // processed.
938 if (nMatchCount==0 || mpSearchItem->GetCommand()==SvxSearchCmd::REPLACE_ALL)
940 ProvideNextTextObject ();
942 if (!mbEndOfSearch && !rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
944 // Remember the current position as the last one with a
945 // text object.
946 maLastValidPosition = maCurrentPosition;
948 // Now that the mbEndOfSearch flag guards this block the
949 // following assertion and return should not be
950 // necessary anymore.
951 DBG_ASSERT(GetEditEngine().HasView(&getOutlinerView()->GetEditView() ),
952 "SearchAndReplace without valid view!" );
953 if ( ! GetEditEngine().HasView( &getOutlinerView()->GetEditView() )
954 && maCurrentPosition.mePageKind != PageKind::Notes )
956 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
957 return true;
960 if (meMode == SEARCH)
962 auto nMatch = getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
963 if (nMatch && maCurrentPosition.meEditMode == EditMode::Page
964 && maCurrentPosition.mePageKind == PageKind::Notes)
966 if(auto pNotesPaneOutliner = lclGetNotesPaneOutliner(pViewShell))
968 pNotesPaneOutliner->SetSelection(getOutlinerView()->GetSelection());
975 else if (nullptr != dynamic_cast<const sd::OutlineViewShell*>(pViewShell.get()))
977 mpDrawDocument->GetDocSh()->SetWaitCursor(false);
978 // The following loop is executed more than once only when a
979 // wrap around search is done.
980 while (true)
982 int nResult = getOutlinerView()->StartSearchAndReplace(*mpSearchItem);
983 if (nResult == 0)
985 if (HandleFailedSearch ())
987 getOutlinerView()->SetSelection (GetSearchStartPosition ());
988 continue;
991 else
992 mbStringFound = true;
993 break;
998 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1000 if (pViewShell && comphelper::LibreOfficeKit::isActive() && mbStringFound)
1002 sendLOKSearchResultCallback(pViewShell, getOutlinerView(), pSelections);
1005 return mbEndOfSearch;
1008 /** Try to detect whether the document or the view (shell) has changed since
1009 the last time <member>StartSearchAndReplace()</member> has been called.
1011 void SdOutliner::DetectChange()
1013 sd::outliner::IteratorPosition aPosition (maCurrentPosition);
1015 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1016 std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
1017 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1019 std::shared_ptr<sd::ViewShell> pOverridingViewShell{};
1020 if(sd::ViewShellBase* pBase = getViewShellBase())
1022 if (const std::shared_ptr<sd::ViewShellManager>& pViewShellManager = pBase->GetViewShellManager())
1023 pOverridingViewShell = pViewShellManager->GetOverridingMainShell();
1026 bool bViewChanged = false;
1028 if( pDrawViewShell )
1030 if( !pOverridingViewShell )
1031 bViewChanged = (aPosition.meEditMode != pDrawViewShell->GetEditMode() || aPosition.mePageKind != pDrawViewShell->GetPageKind());
1032 else
1034 auto pPage = pOverridingViewShell->getCurrentPage();
1035 auto ePageKind = pPage ? pPage->GetPageKind() : PageKind::Standard;
1036 auto eEditMode = EditMode::Page;
1037 bViewChanged = (aPosition.meEditMode != eEditMode || aPosition.mePageKind != ePageKind);
1041 // Detect whether the view has been switched from the outside.
1042 if( bViewChanged )
1044 // Either the edit mode or the page kind has changed.
1045 SetStatusEventHdl(Link<EditStatus&,void>());
1047 SdrPageView* pPageView = mpView->GetSdrPageView();
1048 if (pPageView != nullptr)
1049 mpView->UnmarkAllObj (pPageView);
1050 mpView->SdrEndTextEdit();
1051 SetUpdateLayout(false);
1052 OutlinerView* pOutlinerView = getOutlinerView();
1053 if (pOutlinerView != nullptr)
1054 pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) );
1055 if (meMode == SPELL)
1056 SetPaperSize( Size(1, 1) );
1057 SetText(OUString(), GetParagraph(0));
1059 RememberStartPosition ();
1061 mnPageCount = mpDrawDocument->GetSdPageCount(pDrawViewShell->GetPageKind());
1062 maObjectIterator = sd::outliner::OutlinerContainer(this).current();
1065 // Detect change of the set of selected objects. If their number has
1066 // changed start again with the first selected object.
1067 else if (DetectSelectionChange())
1069 HandleChangedSelection ();
1070 maObjectIterator = sd::outliner::OutlinerContainer(this).current();
1073 // Detect change of page count. Restart search at first/last page in
1074 // that case.
1075 else if (aPosition.meEditMode == EditMode::Page
1076 && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount)
1078 // The number of pages has changed.
1079 mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind);
1080 maObjectIterator = sd::outliner::OutlinerContainer(this).current();
1082 else if (aPosition.meEditMode == EditMode::MasterPage
1083 && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount)
1085 // The number of master pages has changed.
1086 mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind);
1087 maObjectIterator = sd::outliner::OutlinerContainer(this).current();
1091 bool SdOutliner::DetectSelectionChange()
1093 bool bSelectionHasChanged = false;
1095 // If mpObj is NULL then we have not yet found our first match.
1096 // Detecting a change makes no sense.
1097 if (mpObj != nullptr)
1099 const size_t nMarkCount = mpView ? mpView->GetMarkedObjectList().GetMarkCount() : 0;
1100 switch (nMarkCount)
1102 case 0:
1103 // The selection has changed when previously there have been
1104 // selected objects.
1105 bSelectionHasChanged = mbRestrictSearchToSelection;
1106 break;
1107 case 1:
1108 // Check if the only selected object is not the one that we
1109 // had selected.
1110 if (mpView != nullptr)
1112 SdrMark* pMark = mpView->GetMarkedObjectList().GetMark(0);
1113 if (pMark != nullptr)
1114 bSelectionHasChanged = (mpObj != pMark->GetMarkedSdrObj ());
1116 break;
1117 default:
1118 // We had selected exactly one object.
1119 bSelectionHasChanged = true;
1120 break;
1124 return bSelectionHasChanged;
1127 void SdOutliner::RememberStartPosition()
1129 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1130 if ( ! pViewShell)
1132 OSL_ASSERT(pViewShell);
1133 return;
1136 if ( mnStartPageIndex != sal_uInt16(-1) )
1137 return;
1139 if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
1141 std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
1142 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1143 if (pDrawViewShell != nullptr)
1145 meStartViewMode = pDrawViewShell->GetPageKind();
1146 meStartEditMode = pDrawViewShell->GetEditMode();
1147 mnStartPageIndex = pDrawViewShell->GetCurPagePos();
1150 if (mpView != nullptr)
1152 mpStartEditedObject = mpView->GetTextEditObject();
1153 if (mpStartEditedObject != nullptr)
1155 // Try to retrieve current caret position only when there is an
1156 // edited object.
1157 ::Outliner* pOutliner =
1158 static_cast<sd::DrawView*>(mpView)->GetTextEditOutliner();
1159 if (pOutliner!=nullptr && pOutliner->GetViewCount()>0)
1161 OutlinerView* pOutlinerView = pOutliner->GetView(0);
1162 maStartSelection = pOutlinerView->GetSelection();
1167 else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1169 // Remember the current cursor position.
1170 OutlinerView* pView = GetView(0);
1171 if (pView != nullptr)
1172 pView->GetSelection();
1176 void SdOutliner::RestoreStartPosition()
1178 bool bRestore = true;
1179 // Take a negative start page index as indicator that restoring the
1180 // start position is not requested.
1181 if (mnStartPageIndex == sal_uInt16(-1) )
1182 bRestore = false;
1183 // Don't restore when the view shell is not valid.
1184 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1185 if (pViewShell == nullptr)
1186 bRestore = false;
1188 if (!bRestore)
1189 return;
1191 if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
1193 std::shared_ptr<sd::DrawViewShell> pDrawViewShell (
1194 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1195 SetViewMode (meStartViewMode);
1196 if (pDrawViewShell != nullptr)
1198 SetPage (meStartEditMode, mnStartPageIndex);
1199 mpObj = mpStartEditedObject;
1200 if (mpObj)
1202 PutTextIntoOutliner();
1203 EnterEditMode(false);
1204 if (getOutlinerView())
1205 getOutlinerView()->SetSelection(maStartSelection);
1209 else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1211 // Set cursor to its old position.
1212 OutlinerView* pView = GetView(0);
1213 if (pView != nullptr)
1214 pView->SetSelection (maStartSelection);
1218 namespace
1221 bool lclIsValidTextObject(const sd::outliner::IteratorPosition& rPosition)
1223 auto* pObject = DynCastSdrTextObj( rPosition.mxObject.get().get() );
1224 return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj();
1227 bool isValidVectorGraphicObject(const sd::outliner::IteratorPosition& rPosition)
1229 rtl::Reference<SdrGrafObj> pGraphicObject = dynamic_cast<SdrGrafObj*>(rPosition.mxObject.get().get());
1230 if (pGraphicObject)
1232 auto const& pVectorGraphicData = pGraphicObject->GetGraphic().getVectorGraphicData();
1233 if (pVectorGraphicData && VectorGraphicDataType::Pdf == pVectorGraphicData->getType())
1235 return true;
1238 return false;
1241 } // end anonymous namespace
1244 /** The main purpose of this method is to iterate over all shape objects of
1245 the search area (current selection, current view, or whole document)
1246 until a text object has been found that contains at least one match or
1247 until no such object can be found anymore. These two conditions are
1248 expressed by setting one of the flags <member>mbFoundObject</member> or
1249 <member>mbEndOfSearch</member> to <TRUE/>.
1251 void SdOutliner::ProvideNextTextObject()
1253 mbEndOfSearch = false;
1254 mbFoundObject = false;
1256 // reset the vector search
1257 auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext();
1258 rVectorGraphicSearchContext.reset();
1260 mpView->UnmarkAllObj (mpView->GetSdrPageView());
1263 mpView->SdrEndTextEdit();
1265 catch (const css::uno::Exception&)
1267 DBG_UNHANDLED_EXCEPTION("sd.view");
1269 SetUpdateLayout(false);
1270 OutlinerView* pOutlinerView = getOutlinerView();
1271 if (pOutlinerView != nullptr)
1272 pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) );
1273 if (meMode == SPELL)
1274 SetPaperSize( Size(1, 1) );
1275 SetText(OUString(), GetParagraph(0));
1277 mpSearchSpellTextObj = nullptr;
1279 // Iterate until a valid text object has been found or the search ends.
1282 mpObj = nullptr;
1283 mpParaObj = nullptr;
1285 if (maObjectIterator != sd::outliner::OutlinerContainer(this).end())
1287 maCurrentPosition = *maObjectIterator;
1289 // LOK: do not descent to notes or master pages when searching
1290 bool bForbiddenPage = comphelper::LibreOfficeKit::isActive() && (maCurrentPosition.mePageKind != PageKind::Standard || maCurrentPosition.meEditMode != EditMode::Page);
1292 rVectorGraphicSearchContext.reset();
1294 if (!bForbiddenPage)
1296 // Switch to the current object only if it is a valid text object.
1297 if (lclIsValidTextObject(maCurrentPosition))
1299 // Don't set yet in case of searching: the text object may not match.
1300 if (meMode != SEARCH)
1301 mpObj = SetObject(maCurrentPosition);
1302 else
1303 mpObj = maCurrentPosition.mxObject.get().get();
1305 // Or if the object is a valid graphic object which contains vector graphic
1306 else if (meMode == SEARCH && isValidVectorGraphicObject(maCurrentPosition))
1308 mpObj = maCurrentPosition.mxObject.get().get();
1309 rVectorGraphicSearchContext.mbCurrentIsVectorGraphic = true;
1313 // Advance to the next object
1314 ++maObjectIterator;
1316 if (mpObj)
1318 if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic)
1320 // We know here the object is a SdrGrafObj and that it
1321 // contains a vector graphic
1322 auto* pGraphicObject = static_cast<SdrGrafObj*>(mpObj);
1323 OUString const & rString = mpSearchItem->GetSearchString();
1324 bool bBackwards = mpSearchItem->GetBackward();
1326 VectorGraphicSearchOptions aOptions;
1327 aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin;
1328 aOptions.mbMatchCase = mpSearchItem->GetExact();
1329 aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly();
1331 rVectorGraphicSearchContext.mpVectorGraphicSearch = std::make_unique<VectorGraphicSearch>(pGraphicObject->GetGraphic());
1333 bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions);
1334 if (bResult)
1336 if (bBackwards)
1337 bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous();
1338 else
1339 bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next();
1342 if (bResult)
1344 mpObj = SetObject(maCurrentPosition);
1346 mbStringFound = true;
1347 mbMatchMayExist = true;
1348 mbFoundObject = true;
1350 SdrPageView* pPageView = mpView->GetSdrPageView();
1351 mpView->UnmarkAllObj(pPageView);
1353 std::vector<basegfx::B2DRectangle> aSubSelections;
1354 basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj);
1355 if (!aSubSelection.isEmpty())
1356 aSubSelections.push_back(aSubSelection);
1358 mpView->MarkObj(mpObj, pPageView, false, false, std::move(aSubSelections));
1360 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1362 else
1364 rVectorGraphicSearchContext.reset();
1367 else
1369 PutTextIntoOutliner();
1371 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1372 if (pViewShell != nullptr)
1374 switch (meMode)
1376 case SEARCH:
1377 PrepareSearchAndReplace ();
1378 break;
1379 case SPELL:
1380 PrepareSpellCheck ();
1381 break;
1382 case TEXT_CONVERSION:
1383 PrepareConversion();
1384 break;
1390 else
1392 rVectorGraphicSearchContext.reset();
1394 if (meMode == SEARCH)
1395 // Instead of doing a full-blown SetObject(), which would do the same -- but would also possibly switch pages.
1396 mbStringFound = false;
1398 mbEndOfSearch = true;
1399 EndOfSearch ();
1402 while ( ! (mbFoundObject || mbEndOfSearch));
1405 void SdOutliner::EndOfSearch()
1407 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1408 if ( ! pViewShell)
1410 OSL_ASSERT(pViewShell);
1411 return;
1414 // Before we display a dialog we first jump to where the last valid text
1415 // object was found. All page and view mode switching since then was
1416 // temporary and should not be visible to the user.
1417 if( nullptr == dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1418 SetObject (maLastValidPosition);
1420 if (mbRestrictSearchToSelection)
1421 ShowEndOfSearchDialog ();
1422 else
1424 // When no match has been found so far then terminate the search.
1425 if ( ! mbMatchMayExist)
1427 ShowEndOfSearchDialog ();
1428 mbEndOfSearch = true;
1430 // Ask the user whether to wrap around and continue the search or
1431 // to terminate.
1432 else if (meMode==TEXT_CONVERSION || ShowWrapAroundDialog ())
1434 mbMatchMayExist = false;
1435 // Everything back to beginning (or end?) of the document.
1436 maObjectIterator = sd::outliner::OutlinerContainer(this).begin();
1437 if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
1439 // Set cursor to first character of the document.
1440 OutlinerView* pOutlinerView = getOutlinerView();
1441 if (pOutlinerView != nullptr)
1442 pOutlinerView->SetSelection (GetSearchStartPosition ());
1445 mbEndOfSearch = false;
1447 else
1449 // No wrap around.
1450 mbEndOfSearch = true;
1455 void SdOutliner::ShowEndOfSearchDialog()
1457 if (meMode == SEARCH)
1459 if (!mbStringFound)
1461 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
1462 std::shared_ptr<sd::ViewShell> pViewShell(mpWeakViewShell.lock());
1463 if (pViewShell)
1465 SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
1466 rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, mpSearchItem->GetSearchString().toUtf8());
1470 // don't do anything else for search
1471 return;
1474 OUString aString;
1475 const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
1476 if (rMarkList.GetMarkCount() != 0)
1477 aString = SdResId(STR_END_SPELLING_OBJ);
1478 else
1479 aString = SdResId(STR_END_SPELLING);
1481 // Show the message in an info box that is modal with respect to the whole application.
1482 weld::Window* pParent = GetMessageBoxParent();
1483 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
1484 VclMessageType::Info, VclButtonsType::Ok, aString));
1485 xInfoBox->run();
1488 bool SdOutliner::ShowWrapAroundDialog()
1490 // Determine whether to show the dialog.
1491 if (mpSearchItem)
1493 // When searching display the dialog only for single find&replace.
1494 const SvxSearchCmd nCommand(mpSearchItem->GetCommand());
1495 if (nCommand == SvxSearchCmd::REPLACE || nCommand == SvxSearchCmd::FIND)
1497 if (mbDirectionIsForward)
1498 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
1499 else
1500 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start);
1502 return true;
1504 else
1505 return false;
1508 // show dialog only for spelling
1509 if (meMode != SPELL)
1510 return false;
1512 // The question text depends on the search direction.
1513 bool bImpress = mpDrawDocument && mpDrawDocument->GetDocumentType() == DocumentType::Impress;
1515 TranslateId pStringId;
1516 if (mbDirectionIsForward)
1517 pStringId = bImpress ? STR_SAR_WRAP_FORWARD : STR_SAR_WRAP_FORWARD_DRAW;
1518 else
1519 pStringId = bImpress ? STR_SAR_WRAP_BACKWARD : STR_SAR_WRAP_BACKWARD_DRAW;
1521 // Pop up question box that asks the user whether to wrap around.
1522 // The dialog is made modal with respect to the whole application.
1523 weld::Window* pParent = GetMessageBoxParent();
1524 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParent,
1525 VclMessageType::Question, VclButtonsType::YesNo, SdResId(pStringId)));
1526 sal_uInt16 nBoxResult = xQueryBox->run();
1528 return (nBoxResult == RET_YES);
1531 void SdOutliner::PutTextIntoOutliner()
1533 mpSearchSpellTextObj = DynCastSdrTextObj( mpObj );
1534 if ( mpSearchSpellTextObj && mpSearchSpellTextObj->HasText() && !mpSearchSpellTextObj->IsEmptyPresObj() )
1536 SdrText* pText = mpSearchSpellTextObj->getText( maCurrentPosition.mnText );
1537 mpParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
1539 if (mpParaObj != nullptr)
1541 SetText(*mpParaObj);
1543 ClearModifyFlag();
1546 else
1548 mpSearchSpellTextObj = nullptr;
1552 void SdOutliner::PrepareSpellCheck()
1554 EESpellState eState = HasSpellErrors();
1555 DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker");
1557 if (eState == EESpellState::Ok)
1558 return;
1560 // When spell checking we have to test whether we have processed the
1561 // whole document and have reached the start page again.
1562 if (meMode == SPELL)
1564 if (maSearchStartPosition == sd::outliner::Iterator())
1565 // Remember the position of the first text object so that we
1566 // know when we have processed the whole document.
1567 maSearchStartPosition = maObjectIterator;
1568 else if (maSearchStartPosition == maObjectIterator)
1570 mbEndOfSearch = true;
1574 EnterEditMode( false );
1577 void SdOutliner::PrepareSearchAndReplace()
1579 if (!HasText( *mpSearchItem ))
1580 return;
1582 // Set the object now that we know it matches.
1583 mpObj = SetObject(maCurrentPosition);
1585 mbStringFound = true;
1586 mbMatchMayExist = true;
1588 EnterEditMode(false);
1590 mpDrawDocument->GetDocSh()->SetWaitCursor(false);
1592 OutlinerView* pOutlinerView = getOutlinerView();
1593 if (pOutlinerView != nullptr)
1595 pOutlinerView->SetSelection (GetSearchStartPosition ());
1596 if (lclIsValidTextObject(maCurrentPosition) && maCurrentPosition.mePageKind == PageKind::Notes)
1598 if (auto pNotesPaneOutliner = lclGetNotesPaneOutliner(mpWeakViewShell.lock()))
1600 pNotesPaneOutliner->SetSelection(getOutlinerView()->GetSelection());
1606 void SdOutliner::SetViewMode (PageKind ePageKind)
1608 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1609 std::shared_ptr<sd::DrawViewShell> pDrawViewShell(
1610 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1611 if (pDrawViewShell == nullptr || ePageKind == pDrawViewShell->GetPageKind())
1612 return;
1614 // Restore old edit mode.
1615 pDrawViewShell->ChangeEditMode(mpImpl->meOriginalEditMode, false);
1617 SetStatusEventHdl(Link<EditStatus&,void>());
1618 OUString sViewURL;
1619 switch (ePageKind)
1621 case PageKind::Standard:
1622 default:
1623 sViewURL = sd::framework::FrameworkHelper::msImpressViewURL;
1624 break;
1625 case PageKind::Notes:
1626 sViewURL = sd::framework::FrameworkHelper::msNotesViewURL;
1627 break;
1628 case PageKind::Handout:
1629 sViewURL = sd::framework::FrameworkHelper::msHandoutViewURL;
1630 break;
1632 // The text object iterator is destroyed when the shells are
1633 // switched but we need it so save it and restore it afterwards.
1634 sd::outliner::Iterator aIterator (maObjectIterator);
1635 bool bMatchMayExist = mbMatchMayExist;
1637 sd::ViewShellBase& rBase = pViewShell->GetViewShellBase();
1639 rtl::Reference<sd::FuSearch> xFuSearch;
1640 if (pViewShell->GetView())
1641 xFuSearch = pViewShell->GetView()->getSearchContext().getFunctionSearch();
1643 SetViewShell(std::shared_ptr<sd::ViewShell>());
1644 sd::framework::FrameworkHelper::Instance(rBase)->RequestView(
1645 sViewURL,
1646 sd::framework::FrameworkHelper::msCenterPaneURL);
1648 // Force (well, request) a synchronous update of the configuration.
1649 // In a better world we would handle the asynchronous view update
1650 // instead. But that would involve major restructuring of the
1651 // Outliner code.
1652 sd::framework::FrameworkHelper::Instance(rBase)->RequestSynchronousUpdate();
1654 auto pNewViewShell = rBase.GetMainViewShell();
1655 SetViewShell(pNewViewShell);
1656 if (xFuSearch.is() && pNewViewShell->GetView())
1657 pNewViewShell->GetView()->getSearchContext().setSearchFunction(xFuSearch);
1659 // Switching to another view shell has intermediatly called
1660 // EndSpelling(). A PrepareSpelling() is pending, so call that now.
1661 PrepareSpelling();
1663 // Update the number of pages so that
1664 // <member>DetectChange()</member> has the correct value to compare
1665 // to.
1666 mnPageCount = mpDrawDocument->GetSdPageCount(ePageKind);
1668 maObjectIterator = std::move(aIterator);
1669 mbMatchMayExist = bMatchMayExist;
1671 // Save edit mode so that it can be restored when switching the view
1672 // shell again.
1673 pDrawViewShell = std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell);
1674 OSL_ASSERT(pDrawViewShell != nullptr);
1675 if (pDrawViewShell != nullptr)
1676 mpImpl->meOriginalEditMode = pDrawViewShell->GetEditMode();
1679 void SdOutliner::SetPage (EditMode eEditMode, sal_uInt16 nPageIndex)
1681 if ( ! mbRestrictSearchToSelection)
1683 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1684 std::shared_ptr<sd::DrawViewShell> pDrawViewShell(
1685 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell));
1686 OSL_ASSERT(pDrawViewShell != nullptr);
1687 if (pDrawViewShell != nullptr)
1689 pDrawViewShell->ChangeEditMode(eEditMode, false);
1690 pDrawViewShell->SwitchPage(nPageIndex);
1695 void SdOutliner::EnterEditMode (bool bGrabFocus)
1697 OutlinerView* pOutlinerView = getOutlinerView();
1698 if (!(pOutlinerView && mpSearchSpellTextObj))
1699 return;
1701 pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1)));
1702 SetPaperSize( mpSearchSpellTextObj->GetLogicRect().GetSize() );
1703 SdrPageView* pPV = mpView->GetSdrPageView();
1705 // Make FuText the current function.
1706 SfxUInt16Item aItem (SID_TEXTEDIT, 1);
1707 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1708 if (!(pViewShell && pViewShell->GetDispatcher()))
1709 return;
1711 pViewShell->GetDispatcher()->ExecuteList(
1712 SID_TEXTEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, {&aItem});
1714 if (mpView->IsTextEdit())
1716 // end text edition before starting it again
1717 mpView->SdrEndTextEdit();
1720 // To be consistent with the usual behaviour in the Office the text
1721 // object that is put into edit mode would have also to be selected.
1722 // Starting the text edit mode is not enough so we do it here by
1723 // hand.
1724 mpView->UnmarkAllObj(pPV);
1725 mpView->MarkObj(mpSearchSpellTextObj, pPV);
1727 mpSearchSpellTextObj->setActiveText(mnText);
1729 // Turn on the edit mode for the text object.
1730 SetUpdateLayout(true);
1732 if(maCurrentPosition.mePageKind == PageKind::Notes
1733 && maCurrentPosition.meEditMode == EditMode::Page)
1735 sd::ViewShellBase& rBase = pViewShell->GetViewShellBase();
1737 sd::framework::FrameworkHelper::Instance(rBase)->RequestView(
1738 sd::framework::FrameworkHelper::msNotesPanelViewURL,
1739 sd::framework::FrameworkHelper::msBottomImpressPaneURL);
1741 auto pInstance = sd::framework::FrameworkHelper::Instance(rBase);
1742 pInstance->RequestSynchronousUpdate();
1744 std::shared_ptr<sd::ViewShell> pNotesPaneShell(pInstance->GetViewShell(sd::framework::FrameworkHelper::msBottomImpressPaneURL));
1745 if(pNotesPaneShell)
1747 pNotesPaneShell->GetParentWindow()->GrabFocus();
1748 pNotesPaneShell->GetContentWindow()->GrabFocus();
1751 else
1753 if (sd::ViewShellBase* pBase = getViewShellBase())
1755 std::shared_ptr<sd::ViewShell> pOverridingViewShell{};
1756 if (auto pViewShellManager = pBase->GetViewShellManager())
1757 pOverridingViewShell = pViewShellManager->GetOverridingMainShell();
1759 if (pOverridingViewShell)
1761 auto pMainViewShell = pBase->GetMainViewShell().get();
1762 pMainViewShell->GetParentWindow()->GrabFocus();
1763 pMainViewShell->GetContentWindow()->GrabFocus();
1764 bGrabFocus = true;
1768 mpView->SdrBeginTextEdit(mpSearchSpellTextObj, pPV, mpWindow, true, this, pOutlinerView,
1769 true, true, bGrabFocus);
1772 mbFoundObject = true;
1775 ESelection SdOutliner::GetSearchStartPosition() const
1777 // The default constructor uses the beginning of the text as default.
1778 ESelection aPosition;
1779 if (!mbDirectionIsForward)
1781 // Retrieve the position after the last character in the last
1782 // paragraph.
1783 sal_Int32 nParagraphCount = GetParagraphCount();
1784 if (nParagraphCount != 0)
1786 sal_Int32 nLastParagraphLength = GetEditEngine().GetTextLen (
1787 nParagraphCount-1);
1788 aPosition = ESelection (nParagraphCount-1, nLastParagraphLength);
1792 return aPosition;
1795 bool SdOutliner::HasNoPreviousMatch()
1797 OutlinerView* pOutlinerView = getOutlinerView();
1799 assert(pOutlinerView && "outline view in SdOutliner::HasNoPreviousMatch is NULL");
1801 // Detect whether the cursor stands at the beginning
1802 // resp. at the end of the text.
1803 return pOutlinerView->GetSelection() == GetSearchStartPosition();
1806 bool SdOutliner::HandleFailedSearch()
1808 bool bContinueSearch = false;
1810 OutlinerView* pOutlinerView = getOutlinerView();
1811 if (pOutlinerView && mpSearchItem)
1813 // Detect whether there is/may be a prior match. If there is then
1814 // ask the user whether to wrap around. Otherwise tell the user
1815 // that there is no match.
1816 if (HasNoPreviousMatch ())
1818 // No match found in the whole presentation.
1819 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
1822 else
1824 // No further matches found. Ask the user whether to wrap
1825 // around and start again.
1826 bContinueSearch = ShowWrapAroundDialog();
1830 return bContinueSearch;
1833 SdrObject* SdOutliner::SetObject (
1834 const sd::outliner::IteratorPosition& rPosition)
1836 if(rPosition.meEditMode == EditMode::Page && rPosition.mePageKind == PageKind::Notes)
1838 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1839 if (std::shared_ptr<sd::DrawViewShell> pDrawViewShell =
1840 std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell))
1842 if (pDrawViewShell->GetEditMode() != EditMode::Page
1843 || pDrawViewShell->GetCurPagePos() != rPosition.mnPageIndex)
1844 SetPage(EditMode::Page, static_cast<sal_uInt16>(rPosition.mnPageIndex));
1846 mnText = rPosition.mnText;
1847 return rPosition.mxObject.get().get();
1849 else
1851 SetViewMode(rPosition.mePageKind);
1852 SetPage(rPosition.meEditMode, static_cast<sal_uInt16>(rPosition.mnPageIndex));
1855 mnText = rPosition.mnText;
1856 return rPosition.mxObject.get().get();
1859 void SdOutliner::SetViewShell (const std::shared_ptr<sd::ViewShell>& rpViewShell)
1861 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1862 if (pViewShell == rpViewShell)
1863 return;
1865 // Set the new view shell.
1866 mpWeakViewShell = rpViewShell;
1867 // When the outline view is not owned by us then we have to clear
1868 // that pointer so that the current one for the new view shell will
1869 // be used (in ProvideOutlinerView).
1870 if (rpViewShell)
1872 mpView = rpViewShell->GetView();
1874 mpWindow = rpViewShell->GetActiveWindow();
1876 mpImpl->ProvideOutlinerView(*this, rpViewShell, mpWindow);
1877 OutlinerView* pOutlinerView = getOutlinerView();
1878 if (pOutlinerView != nullptr)
1879 pOutlinerView->SetWindow(mpWindow);
1881 else
1883 mpView = nullptr;
1884 mpWindow = nullptr;
1888 void SdOutliner::HandleChangedSelection()
1890 maMarkListCopy.clear();
1891 const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
1892 mbRestrictSearchToSelection = rMarkList.GetMarkCount() != 0;
1893 if (!mbRestrictSearchToSelection)
1894 return;
1896 // Make a copy of the current mark list.
1897 const size_t nCount = rMarkList.GetMarkCount();
1898 if (nCount > 0)
1900 maMarkListCopy.clear();
1901 maMarkListCopy.reserve (nCount);
1902 for (size_t i=0; i<nCount; ++i)
1903 maMarkListCopy.emplace_back(rMarkList.GetMark(i)->GetMarkedSdrObj ());
1905 else
1906 // No marked object. Is this case possible?
1907 mbRestrictSearchToSelection = false;
1910 void SdOutliner::StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage,
1911 const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive )
1913 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1914 bool bMultiDoc = nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() );
1916 meMode = TEXT_CONVERSION;
1917 mbDirectionIsForward = true;
1918 mpSearchItem.reset();
1919 mnConversionLanguage = nSourceLanguage;
1921 BeginConversion();
1923 OutlinerView* pOutlinerView = getOutlinerView();
1924 if (pOutlinerView != nullptr)
1926 pOutlinerView->StartTextConversion(
1927 GetMessageBoxParent(),
1928 nSourceLanguage,
1929 nTargetLanguage,
1930 pTargetFont,
1931 nOptions,
1932 bIsInteractive,
1933 bMultiDoc);
1936 EndConversion();
1939 /** Prepare to do a text conversion on the current text object. This
1940 includes putting it into edit mode.
1942 void SdOutliner::PrepareConversion()
1944 SetUpdateLayout(true);
1945 if( HasConvertibleTextPortion( mnConversionLanguage ) )
1947 SetUpdateLayout(false);
1948 mbStringFound = true;
1949 mbMatchMayExist = true;
1951 EnterEditMode(true);
1953 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
1954 // Start search at the right end of the current object's text
1955 // depending on the search direction.
1957 else
1959 SetUpdateLayout(false);
1963 void SdOutliner::BeginConversion()
1965 SetRefDevice(SdModule::get()->GetVirtualRefDevice());
1967 sd::ViewShellBase* pBase = getViewShellBase();
1968 if (pBase != nullptr)
1969 SetViewShell (pBase->GetMainViewShell());
1971 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
1972 if (pViewShell)
1974 mbStringFound = false;
1976 // Supposed that we are not located at the very beginning/end of the
1977 // document then there may be a match in the document prior/after
1978 // the current position.
1979 mbMatchMayExist = true;
1981 maObjectIterator = sd::outliner::Iterator();
1982 maSearchStartPosition = sd::outliner::Iterator();
1983 RememberStartPosition();
1985 mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow);
1987 HandleChangedSelection ();
1989 ClearModifyFlag();
1992 void SdOutliner::EndConversion()
1994 EndSpelling();
1997 bool SdOutliner::ConvertNextDocument()
1999 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
2000 if (dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ) )
2001 return false;
2003 mpDrawDocument->GetDocSh()->SetWaitCursor( true );
2005 Initialize ( true );
2007 OutlinerView* pOutlinerView = getOutlinerView();
2008 if (pOutlinerView != nullptr)
2010 mpWindow = pViewShell->GetActiveWindow();
2011 pOutlinerView->SetWindow(mpWindow);
2013 ProvideNextTextObject ();
2015 mpDrawDocument->GetDocSh()->SetWaitCursor( false );
2016 ClearModifyFlag();
2018 // for text conversion we automatically wrap around one
2019 // time and stop at the start shape
2020 if( mpFirstObj )
2022 if( (mnText == 0) && (mpFirstObj == mpObj) )
2023 return false;
2025 else
2027 mpFirstObj = mpObj;
2030 return !mbEndOfSearch;
2033 weld::Window* SdOutliner::GetMessageBoxParent()
2035 // We assume that the parent of the given message box is NULL, i.e. it is
2036 // modal with respect to the top application window. However, this
2037 // does not affect the search dialog. Therefore we have to lock it here
2038 // while the message box is being shown. We also have to take into
2039 // account that we are called during a spell check and the search dialog
2040 // is not available.
2041 weld::Window* pSearchDialog = nullptr;
2042 SfxChildWindow* pChildWindow = nullptr;
2043 switch (meMode)
2045 case SEARCH:
2046 if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
2047 pChildWindow = pViewFrm->GetChildWindow(
2048 SvxSearchDialogWrapper::GetChildWindowId());
2049 break;
2051 case SPELL:
2052 if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
2053 pChildWindow = pViewFrm->GetChildWindow(
2054 sd::SpellDialogChildWindow::GetChildWindowId());
2055 break;
2057 case TEXT_CONVERSION:
2058 // There should no messages boxes be displayed while doing the
2059 // hangul hanja conversion.
2060 break;
2063 if (pChildWindow != nullptr)
2065 auto xController = pChildWindow->GetController();
2066 pSearchDialog = xController ? xController->getDialog() : nullptr;
2069 if (pSearchDialog)
2070 return pSearchDialog;
2072 std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
2073 auto pWin = pViewShell->GetActiveWindow();
2074 return pWin ? pWin->GetFrameWeld() : nullptr;
2077 //===== SdOutliner::Implementation ==============================================
2079 SdOutliner::Implementation::Implementation()
2080 : meOriginalEditMode(EditMode::Page),
2081 mbOwnOutlineView(false),
2082 mpOutlineView(nullptr)
2086 SdOutliner::Implementation::~Implementation()
2088 if (mbOwnOutlineView && mpOutlineView!=nullptr)
2090 mpOutlineView->SetWindow(nullptr);
2091 delete mpOutlineView;
2092 mpOutlineView = nullptr;
2096 /** We try to create a new OutlinerView only when there is none available,
2097 either from an OutlinerViewShell or a previous call to
2098 ProvideOutlinerView(). This is necessary to support the spell checker
2099 which can not cope with exchanging the OutlinerView.
2101 void SdOutliner::Implementation::ProvideOutlinerView (
2102 Outliner& rOutliner,
2103 const std::shared_ptr<sd::ViewShell>& rpViewShell,
2104 vcl::Window* pWindow)
2106 if (rpViewShell == nullptr)
2107 return;
2109 switch (rpViewShell->GetShellType())
2111 case sd::ViewShell::ST_DRAW:
2112 case sd::ViewShell::ST_IMPRESS:
2113 case sd::ViewShell::ST_NOTES:
2114 case sd::ViewShell::ST_HANDOUT:
2116 // Create a new outline view to do the search on.
2117 bool bInsert = false;
2118 if (mpOutlineView != nullptr && !mbOwnOutlineView)
2119 mpOutlineView = nullptr;
2121 if (mpOutlineView == nullptr || !rOutliner.GetEditEngine().HasView(&mpOutlineView->GetEditView()))
2123 delete mpOutlineView;
2124 mpOutlineView = new OutlinerView(&rOutliner, pWindow);
2125 mbOwnOutlineView = true;
2126 bInsert = true;
2128 else
2129 mpOutlineView->SetWindow(pWindow);
2131 EVControlBits nStat = mpOutlineView->GetControlWord();
2132 nStat &= ~EVControlBits::AUTOSCROLL;
2133 mpOutlineView->SetControlWord(nStat);
2135 if (bInsert)
2136 rOutliner.InsertView( mpOutlineView );
2138 rOutliner.SetUpdateLayout(false);
2139 mpOutlineView->SetOutputArea (::tools::Rectangle (Point(), Size(1, 1)));
2140 rOutliner.SetPaperSize( Size(1, 1) );
2141 rOutliner.SetText(OUString(), rOutliner.GetParagraph(0));
2143 meOriginalEditMode =
2144 std::static_pointer_cast<sd::DrawViewShell>(rpViewShell)->GetEditMode();
2146 break;
2148 case sd::ViewShell::ST_OUTLINE:
2150 if (mpOutlineView!=nullptr && mbOwnOutlineView)
2151 delete mpOutlineView;
2152 mpOutlineView = rOutliner.GetView(0);
2153 mbOwnOutlineView = false;
2155 break;
2157 default:
2158 case sd::ViewShell::ST_NONE:
2159 case sd::ViewShell::ST_PRESENTATION:
2160 // Ignored
2161 break;
2165 void SdOutliner::Implementation::ReleaseOutlinerView()
2167 if (mbOwnOutlineView)
2169 OutlinerView* pView = mpOutlineView;
2170 mpOutlineView = nullptr;
2171 mbOwnOutlineView = false;
2172 if (pView != nullptr)
2174 pView->SetWindow(nullptr);
2175 delete pView;
2178 else
2180 mpOutlineView = nullptr;
2184 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */