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