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 <OutlineView.hxx>
21 #include <sfx2/progress.hxx>
22 #include <vcl/commandinfoprovider.hxx>
23 #include <vcl/svapp.hxx>
24 #include <svx/svxids.hrc>
25 #include <editeng/outliner.hxx>
26 #include <editeng/eeitem.hxx>
27 #include <editeng/editstat.hxx>
28 #include <editeng/lrspitem.hxx>
29 #include <svx/svdotext.hxx>
30 #include <sfx2/viewfrm.hxx>
31 #include <svl/style.hxx>
32 #include <svx/svdundo.hxx>
33 #include <editeng/numitem.hxx>
34 #include <editeng/outlobj.hxx>
35 #include <editeng/editeng.hxx>
36 #include <xmloff/autolayout.hxx>
37 #include <tools/debug.hxx>
38 #include <officecfg/Office/Common.hxx>
39
40 #include <editeng/editobj.hxx>
41 #include <editeng/editund2.hxx>
42
43 #include <editeng/editview.hxx>
44
45 #include <com/sun/star/frame/XFrame.hpp>
46
47 #include <DrawDocShell.hxx>
48 #include <drawdoc.hxx>
49 #include <Window.hxx>
50 #include <sdpage.hxx>
51 #include <pres.hxx>
52 #include <OutlineViewShell.hxx>
53 #include <app.hrc>
54 #include <strings.hrc>
55 #include <sdmod.hxx>
56 #include <sdresid.hxx>
57 #include <Outliner.hxx>
58 #include <EventMultiplexer.hxx>
59 #include <ViewShellBase.hxx>
60 #include <ViewShellManager.hxx>
61 #include <undo/undomanager.hxx>
62 #include <stlsheet.hxx>
63
64 using namespace ::com::sun::star::uno;
65 using namespace ::com::sun::star::frame;
66
67 namespace sd {
68
69 // a progress bar gets displayed when more than
70 // PROCESS_WITH_PROGRESS_THRESHOLD pages are concerned
71 #define PROCESS_WITH_PROGRESS_THRESHOLD 5
72
OutlineView(DrawDocShell & rDocSh,vcl::Window * pWindow,OutlineViewShell & rOutlineViewShell)73 OutlineView::OutlineView( DrawDocShell& rDocSh, vcl::Window* pWindow, OutlineViewShell& rOutlineViewShell)
74 : ::sd::SimpleOutlinerView(*rDocSh.GetDoc(), pWindow->GetOutDev(), &rOutlineViewShell)
75 , mrOutlineViewShell(rOutlineViewShell)
76 , mrOutliner(*mrDoc.GetOutliner())
77 , mnPagesToProcess(0)
78 , mnPagesProcessed(0)
79 , mbFirstPaint(true)
80 , maDocColor( COL_WHITE )
81 , maLRSpaceItem(2000, 0, 0, EE_PARA_OUTLLRSPACE)
82 {
83 bool bInitOutliner = false;
84
85 if (mrOutliner.GetViewCount() == 0)
86 {
87 // initialize Outliner: set Reference Device
88 bInitOutliner = true;
89 mrOutliner.Init( OutlinerMode::OutlineView );
90 mrOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() );
91 //viewsize without the width of the image and number in front
92 mnPaperWidth = (mrOutlineViewShell.GetActiveWindow()->GetViewSize().Width() - 4000);
93 mrOutliner.SetPaperSize(Size(mnPaperWidth, 400000000));
94 }
95 else
96 {
97 // width: DIN A4, two margins at 1 cm each
98 mnPaperWidth = 19000;
99 }
100
101 mpOutlinerViews[0].reset( new OutlinerView(&mrOutliner, pWindow) );
102 mpOutlinerViews[0]->SetOutputArea(::tools::Rectangle());
103 mrOutliner.SetUpdateLayout(false);
104 mrOutliner.InsertView(mpOutlinerViews[0].get(), EE_APPEND);
105
106 onUpdateStyleSettings( true );
107
108 if (bInitOutliner)
109 {
110 // fill Outliner with contents
111 FillOutliner();
112 }
113
114 Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,OutlineView,EventMultiplexerListener) );
115 mrOutlineViewShell.GetViewShellBase().GetEventMultiplexer()->AddEventListener(aLink);
116
117 Reference<XFrame> xFrame;
118 if (SfxViewFrame* pFrame = mrOutlineViewShell.GetViewShellBase().GetFrame())
119 xFrame = pFrame->GetFrame().GetFrameInterface();
120 maSlideImage = vcl::CommandInfoProvider::GetImageForCommand(u".uno:ShowSlide"_ustr, xFrame, vcl::ImageType::Size26);
121
122 // Tell undo manager of the document about the undo manager of the
123 // outliner, so that the former can synchronize with the later.
124 sd::UndoManager* pDocUndoMgr = dynamic_cast<sd::UndoManager*>(mpDocSh->GetUndoManager());
125 if (pDocUndoMgr != nullptr)
126 pDocUndoMgr->SetLinkedUndoManager(&mrOutliner.GetUndoManager());
127 }
128
129 /**
130 * Destructor, restore Links, clear Outliner
131 */
~OutlineView()132 OutlineView::~OutlineView()
133 {
134 DBG_ASSERT(maDragAndDropModelGuard == nullptr,
135 "sd::OutlineView::~OutlineView(), prior drag operation not finished correctly!");
136
137 Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,OutlineView,EventMultiplexerListener) );
138 mrOutlineViewShell.GetViewShellBase().GetEventMultiplexer()->RemoveEventListener( aLink );
139 DisconnectFromApplication();
140
141 mpProgress.reset();
142
143 // unregister OutlinerViews and destroy them
144 for (auto & rpView : mpOutlinerViews)
145 {
146 if (rpView)
147 {
148 mrOutliner.RemoveView( rpView.get() );
149 rpView.reset();
150 }
151 }
152
153 if (mrOutliner.GetViewCount() == 0)
154 {
155 // uninitialize Outliner: enable color display
156 ResetLinks();
157 EEControlBits nCntrl = mrOutliner.GetControlWord();
158 mrOutliner.SetUpdateLayout(false); // otherwise there will be drawn on SetControlWord
159 mrOutliner.SetControlWord(nCntrl & ~EEControlBits::NOCOLORS);
160 mrOutliner.ForceAutoColor( officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get() );
161 mrOutliner.Clear();
162 }
163 }
164
ConnectToApplication()165 void OutlineView::ConnectToApplication()
166 {
167 // When the mode is switched to outline the main view shell grabs focus.
168 // This is done for getting cut/copy/paste commands on slides in the left
169 // pane (slide sorter view shell) to work properly.
170 SfxShell* pTopViewShell = mrOutlineViewShell.GetViewShellBase().GetViewShellManager()->GetTopViewShell();
171 if (pTopViewShell && pTopViewShell == &mrOutlineViewShell)
172 {
173 mrOutlineViewShell.GetActiveWindow()->GrabFocus();
174 }
175
176 Application::AddEventListener(LINK(this, OutlineView, AppEventListenerHdl));
177 }
178
DisconnectFromApplication()179 void OutlineView::DisconnectFromApplication()
180 {
181 Application::RemoveEventListener(LINK(this, OutlineView, AppEventListenerHdl));
182 }
183
Paint(const::tools::Rectangle & rRect,::sd::Window const * pWin)184 void OutlineView::Paint(const ::tools::Rectangle& rRect, ::sd::Window const * pWin)
185 {
186 OutlinerView* pOlView = GetViewByWindow(pWin);
187
188 if (pOlView)
189 {
190 pOlView->HideCursor();
191 pOlView->Paint(rRect);
192
193 pOlView->ShowCursor(mbFirstPaint);
194
195 mbFirstPaint = false;
196 }
197 }
198
AddDeviceToPaintView(OutputDevice & rDev,vcl::Window * pWindow)199 void OutlineView::AddDeviceToPaintView(OutputDevice& rDev, vcl::Window* pWindow)
200 {
201 bool bAdded = false;
202 bool bValidArea = false;
203 ::tools::Rectangle aOutputArea;
204 const Color aWhiteColor( COL_WHITE );
205 sal_uInt16 nView = 0;
206
207 while (nView < MAX_OUTLINERVIEWS && !bAdded)
208 {
209 if (mpOutlinerViews[nView] == nullptr)
210 {
211 mpOutlinerViews[nView].reset( new OutlinerView(&mrOutliner, dynamic_cast< ::sd::Window* >(rDev.GetOwnerWindow())) );
212 mpOutlinerViews[nView]->SetBackgroundColor( aWhiteColor );
213 mrOutliner.InsertView(mpOutlinerViews[nView].get(), EE_APPEND);
214 bAdded = true;
215
216 if (bValidArea)
217 {
218 mpOutlinerViews[nView]->SetOutputArea(aOutputArea);
219 }
220 }
221 else if (!bValidArea)
222 {
223 aOutputArea = mpOutlinerViews[nView]->GetOutputArea();
224 bValidArea = true;
225 }
226
227 nView++;
228 }
229
230 // white background in Outliner
231 rDev.SetBackground( Wallpaper( aWhiteColor ) );
232
233 ::sd::View::AddDeviceToPaintView(rDev, pWindow);
234 }
235
DeleteDeviceFromPaintView(OutputDevice & rDev)236 void OutlineView::DeleteDeviceFromPaintView(OutputDevice& rDev)
237 {
238 bool bRemoved = false;
239 sal_uInt16 nView = 0;
240 vcl::Window* pWindow;
241
242 while (nView < MAX_OUTLINERVIEWS && !bRemoved)
243 {
244 if (mpOutlinerViews[nView] != nullptr)
245 {
246 pWindow = mpOutlinerViews[nView]->GetWindow();
247
248 if (pWindow->GetOutDev() == &rDev)
249 {
250 mrOutliner.RemoveView( mpOutlinerViews[nView].get() );
251 mpOutlinerViews[nView].reset();
252 bRemoved = true;
253 }
254 }
255
256 nView++;
257 }
258
259 ::sd::View::DeleteDeviceFromPaintView(rDev);
260 }
261
262 /**
263 * Return a pointer to the OutlinerView corresponding to the window
264 */
GetViewByWindow(vcl::Window const * pWin) const265 OutlinerView* OutlineView::GetViewByWindow (vcl::Window const * pWin) const
266 {
267 OutlinerView* pOlView = nullptr;
268 for (std::unique_ptr<OutlinerView> const & pView : mpOutlinerViews)
269 {
270 if (pView != nullptr)
271 {
272 if ( pWin == pView->GetWindow() )
273 {
274 pOlView = pView.get();
275 }
276 }
277 }
278 return pOlView;
279 }
280
281 /**
282 * Return the title before a random paragraph
283 */
GetPrevTitle(const Paragraph * pPara)284 Paragraph* OutlineView::GetPrevTitle(const Paragraph* pPara)
285 {
286 sal_Int32 nPos = mrOutliner.GetAbsPos(pPara);
287
288 if (nPos > 0)
289 {
290 while(nPos)
291 {
292 pPara = mrOutliner.GetParagraph(--nPos);
293 if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) )
294 {
295 return const_cast< Paragraph* >( pPara );
296 }
297 }
298
299 }
300 return nullptr;
301 }
302
303 /**
304 * Return the title after a random paragraph
305 */
GetNextTitle(const Paragraph * pPara)306 Paragraph* OutlineView::GetNextTitle(const Paragraph* pPara)
307 {
308 Paragraph* pResult = const_cast< Paragraph* >( pPara );
309
310 sal_Int32 nPos = mrOutliner.GetAbsPos(pResult);
311
312 do
313 {
314 pResult = mrOutliner.GetParagraph(++nPos);
315 if( pResult && ::Outliner::HasParaFlag(pResult, ParaFlag::ISPAGE) )
316 return pResult;
317 }
318 while( pResult );
319
320 return nullptr;
321 }
322
323 /**
324 * Handler for inserting pages (paragraphs)
325 */
IMPL_LINK(OutlineView,ParagraphInsertedHdl,Outliner::ParagraphHdlParam,aParam,void)326 IMPL_LINK( OutlineView, ParagraphInsertedHdl, Outliner::ParagraphHdlParam, aParam, void )
327 {
328 // we get calls to this handler during binary insert of drag and drop contents but
329 // we ignore it here and handle it later in OnEndPasteOrDrop()
330 if (maDragAndDropModelGuard != nullptr)
331 return;
332
333 OutlineViewPageChangesGuard aGuard(this);
334
335 sal_Int32 nAbsPos = mrOutliner.GetAbsPos( aParam.pPara );
336
337 UpdateParagraph( nAbsPos );
338
339 if( (nAbsPos == 0) ||
340 ::Outliner::HasParaFlag(aParam.pPara, ParaFlag::ISPAGE) ||
341 ::Outliner::HasParaFlag(mrOutliner.GetParagraph( nAbsPos-1 ), ParaFlag::ISPAGE) )
342 {
343 InsertSlideForParagraph( aParam.pPara );
344 }
345 }
346
347 /** creates and inserts an empty slide for the given paragraph */
InsertSlideForParagraph(Paragraph * pPara)348 SdPage* OutlineView::InsertSlideForParagraph( Paragraph* pPara )
349 {
350 DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::InsertSlideForParagraph(), model change without undo?!" );
351
352 OutlineViewPageChangesGuard aGuard(this);
353
354 mrOutliner.SetParaFlag( pPara, ParaFlag::ISPAGE );
355 // how many titles are there before the new title paragraph?
356 sal_uLong nExample = 0; // position of the "example" page
357 sal_uLong nTarget = 0; // position of insertion
358 while(pPara)
359 {
360 pPara = GetPrevTitle(pPara);
361 if (pPara)
362 nTarget++;
363 }
364
365 // if a new paragraph is created via RETURN before the first paragraph, the
366 // Outliner reports the old paragraph (which was moved down) as a new
367 // paragraph
368 if (nTarget == 1)
369 {
370 OUString aTest = mrOutliner.GetText(mrOutliner.GetParagraph(0));
371 if (aTest.isEmpty())
372 {
373 nTarget = 0;
374 }
375 }
376
377 // the "example" page is the previous page - if it is available
378 if (nTarget > 0)
379 {
380 nExample = nTarget - 1;
381
382 sal_uInt16 nPageCount = mrDoc.GetSdPageCount( PageKind::Standard );
383 if( nExample >= nPageCount )
384 nExample = nPageCount - 1;
385 }
386
387 /**********************************************************************
388 * All the time, a standard page is created before a notes page.
389 * It is ensured that after each standard page the corresponding notes page
390 * follows. A handout page is exactly one handout page.
391 **********************************************************************/
392
393 // this page is exemplary
394 SdPage* pExample = mrDoc.GetSdPage(static_cast<sal_uInt16>(nExample), PageKind::Standard);
395 rtl::Reference<SdPage> pPage = mrDoc.AllocSdPage(false);
396
397 pPage->SetLayoutName(pExample->GetLayoutName());
398
399 // insert (page)
400 mrDoc.InsertPage(pPage.get(), static_cast<sal_uInt16>(nTarget) * 2 + 1);
401 if( isRecordingUndo() )
402 AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewPage(*pPage));
403
404 // assign a master page to the standard page
405 pPage->TRG_SetMasterPage(pExample->TRG_GetMasterPage());
406
407 // set page size
408 pPage->SetSize(pExample->GetSize());
409 pPage->SetBorder( pExample->GetLeftBorder(),
410 pExample->GetUpperBorder(),
411 pExample->GetRightBorder(),
412 pExample->GetLowerBorder() );
413
414 // create new presentation objects (after <Title> or <Title with subtitle>
415 // follows <Title with outline>, otherwise apply the layout of the previous
416 // page
417 AutoLayout eAutoLayout = pExample->GetAutoLayout();
418 if (eAutoLayout == AUTOLAYOUT_TITLE ||
419 eAutoLayout == AUTOLAYOUT_TITLE_ONLY)
420 {
421 pPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true);
422 }
423 else
424 {
425 pPage->SetAutoLayout(pExample->GetAutoLayout(), true);
426 }
427
428 /**********************************************************************
429 |* now the notes page
430 \*********************************************************************/
431 pExample = mrDoc.GetSdPage(static_cast<sal_uInt16>(nExample), PageKind::Notes);
432 rtl::Reference<SdPage> pNotesPage = mrDoc.AllocSdPage(false);
433
434 pNotesPage->SetLayoutName(pExample->GetLayoutName());
435
436 pNotesPage->SetPageKind(PageKind::Notes);
437
438 // insert (notes page)
439 mrDoc.InsertPage(pNotesPage.get(), static_cast<sal_uInt16>(nTarget) * 2 + 2);
440 if( isRecordingUndo() )
441 AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage));
442
443 // assign a master page to the notes page
444 pNotesPage->TRG_SetMasterPage(pExample->TRG_GetMasterPage());
445
446 // set page size, there must be already one page available
447 pNotesPage->SetSize(pExample->GetSize());
448 pNotesPage->SetBorder( pExample->GetLeftBorder(),
449 pExample->GetUpperBorder(),
450 pExample->GetRightBorder(),
451 pExample->GetLowerBorder() );
452
453 // create presentation objects
454 pNotesPage->SetAutoLayout(pExample->GetAutoLayout(), true);
455
456 mrOutliner.UpdateFields();
457
458 return pPage.get();
459 }
460
461 /**
462 * Handler for deleting pages (paragraphs)
463 */
IMPL_LINK(OutlineView,ParagraphRemovingHdl,::Outliner::ParagraphHdlParam,aParam,void)464 IMPL_LINK( OutlineView, ParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, aParam, void )
465 {
466 DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::ParagraphRemovingHdl(), model change without undo?!" );
467
468 OutlineViewPageChangesGuard aGuard(this);
469
470 Paragraph* pPara = aParam.pPara;
471 if( !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) )
472 return;
473
474 // how many titles are in front of the title paragraph in question?
475 sal_uLong nPos = 0;
476 while(pPara)
477 {
478 pPara = GetPrevTitle(pPara);
479 if (pPara) nPos++;
480 }
481
482 // delete page and notes page
483 sal_uInt16 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1;
484 SdrPage* pPage = mrDoc.GetPage(nAbsPos);
485 if( isRecordingUndo() )
486 AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage));
487 mrDoc.RemovePage(nAbsPos);
488
489 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1;
490 pPage = mrDoc.GetPage(nAbsPos);
491 if( isRecordingUndo() )
492 AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage));
493 mrDoc.RemovePage(nAbsPos);
494
495 // progress display if necessary
496 if (mnPagesToProcess)
497 {
498 mnPagesProcessed++;
499
500 if(mpProgress)
501 mpProgress->SetState(mnPagesProcessed);
502
503 if (mnPagesProcessed == mnPagesToProcess)
504 {
505 mpProgress.reset();
506 mnPagesToProcess = 0;
507 mnPagesProcessed = 0;
508 }
509 }
510 aParam.pOutliner->UpdateFields();
511 }
512
513 /**
514 * Handler for changing the indentation depth of paragraphs (requires inserting
515 * or deleting of pages in some cases)
516 */
IMPL_LINK(OutlineView,DepthChangedHdl,::Outliner::DepthChangeHdlParam,aParam,void)517 IMPL_LINK( OutlineView, DepthChangedHdl, ::Outliner::DepthChangeHdlParam, aParam, void )
518 {
519 DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::DepthChangedHdl(), no undo for model change?!" );
520
521 OutlineViewPageChangesGuard aGuard(this);
522
523 Paragraph* pPara = aParam.pPara;
524 ::Outliner* pOutliner = aParam.pOutliner;
525 if( ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && ((aParam.nPrevFlags & ParaFlag::ISPAGE) == ParaFlag::NONE) )
526 {
527 // the current paragraph is transformed into a slide
528
529 mrOutliner.SetDepth( pPara, -1 );
530
531 // are multiple level 1 paragraphs being brought to level 0 and we
532 // should start a progress view or a timer and didn't already?
533 if (mnPagesToProcess == 0)
534 {
535 Window* pActWin = mrOutlineViewShell.GetActiveWindow();
536 OutlinerView* pOlView = GetViewByWindow(pActWin);
537
538 std::vector<Paragraph*> aSelList;
539 pOlView->CreateSelectionList(aSelList);
540
541 mnPagesToProcess = std::count_if(aSelList.begin(), aSelList.end(),
542 [&pOutliner](const Paragraph *pParagraph) {
543 return !Outliner::HasParaFlag(pParagraph, ParaFlag::ISPAGE) &&
544 (pOutliner->GetDepth(pOutliner->GetAbsPos(pParagraph)) <= 0);
545 });
546
547 mnPagesToProcess++; // the paragraph being in level 0 already
548 // should be included
549 mnPagesProcessed = 0;
550
551 if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD)
552 {
553 mpProgress.reset( new SfxProgress( GetDocSh(), SdResId(STR_CREATE_PAGES), mnPagesToProcess ) );
554 }
555 else
556 {
557 mpDocSh->SetWaitCursor( true );
558 }
559 }
560
561 ParagraphInsertedHdl( { aParam.pOutliner, aParam.pPara } );
562
563 mnPagesProcessed++;
564
565 // should there be a progress display?
566 if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD)
567 {
568 if (mpProgress)
569 mpProgress->SetState(mnPagesProcessed);
570 }
571
572 // was this the last page?
573 if (mnPagesProcessed == mnPagesToProcess)
574 {
575 if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD && mpProgress)
576 {
577 mpProgress.reset();
578 }
579 else
580 mpDocSh->SetWaitCursor( false );
581
582 mnPagesToProcess = 0;
583 mnPagesProcessed = 0;
584 }
585 pOutliner->UpdateFields();
586 }
587 else if( !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && ((aParam.nPrevFlags & ParaFlag::ISPAGE) != ParaFlag::NONE) )
588 {
589 // the paragraph was a page but now becomes a normal paragraph
590
591 // how many titles are before the title paragraph in question?
592 sal_uLong nPos = 0;
593 Paragraph* pParagraph = pPara;
594 while(pParagraph)
595 {
596 pParagraph = GetPrevTitle(pParagraph);
597 if (pParagraph)
598 nPos++;
599 }
600 // delete page and notes page
601
602 sal_uInt16 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1;
603 SdrPage* pPage = mrDoc.GetPage(nAbsPos);
604 if( isRecordingUndo() )
605 AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage));
606 mrDoc.RemovePage(nAbsPos);
607
608 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1;
609 pPage = mrDoc.GetPage(nAbsPos);
610 if( isRecordingUndo() )
611 AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage));
612 mrDoc.RemovePage(nAbsPos);
613
614 pPage = GetPageForParagraph( pPara );
615
616 mrOutliner.SetDepth( pPara, (pPage && (static_cast<SdPage*>(pPage)->GetAutoLayout() == AUTOLAYOUT_TITLE)) ? -1 : 0 );
617
618 // progress display if necessary
619 if (mnPagesToProcess)
620 {
621 mnPagesProcessed++;
622 if (mpProgress)
623 mpProgress->SetState(mnPagesProcessed);
624
625 if (mnPagesProcessed == mnPagesToProcess)
626 {
627 mpProgress.reset();
628 mnPagesToProcess = 0;
629 mnPagesProcessed = 0;
630 }
631 }
632 pOutliner->UpdateFields();
633 }
634 else if ( (pOutliner->GetPrevDepth() == 1) && ( pOutliner->GetDepth( pOutliner->GetAbsPos( pPara ) ) == 2 ) )
635 {
636 // how many titles are in front of the title paragraph in question?
637 sal_Int32 nPos = -1;
638
639 Paragraph* pParagraph = pPara;
640 while(pParagraph)
641 {
642 pParagraph = GetPrevTitle(pParagraph);
643 if (pParagraph)
644 nPos++;
645 }
646
647 if(nPos >= 0)
648 {
649 SdPage*pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard);
650
651 if(pPage && pPage->GetPresObj(PresObjKind::Text))
652 pOutliner->SetDepth( pPara, 0 );
653 }
654
655 }
656 // how many titles are in front of the title paragraph in question?
657 sal_Int32 nPos = -1;
658
659 Paragraph* pTempPara = pPara;
660 while(pTempPara)
661 {
662 pTempPara = GetPrevTitle(pTempPara);
663 if (pTempPara)
664 nPos++;
665 }
666
667 if( nPos < 0 )
668 return;
669
670 SdPage* pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard );
671
672 if( !pPage )
673 return;
674
675 SfxStyleSheet* pStyleSheet = nullptr;
676 sal_Int32 nPara = pOutliner->GetAbsPos( pPara );
677 sal_Int16 nDepth = pOutliner->GetDepth( nPara );
678 bool bSubTitle = pPage->GetPresObj(PresObjKind::Text) != nullptr;
679
680 if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) )
681 {
682 pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title );
683 }
684 else if( bSubTitle )
685 {
686 pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text );
687 }
688 else
689 {
690 pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline );
691
692 if( nDepth > 0 )
693 {
694 OUString aNewStyleSheetName = pStyleSheet->GetName();
695 if (!aNewStyleSheetName.isEmpty())
696 aNewStyleSheetName = aNewStyleSheetName.copy(0, aNewStyleSheetName.getLength() - 1);
697 aNewStyleSheetName += OUString::number( nDepth+1 );
698 SfxStyleSheetBasePool* pStylePool = mrDoc.GetStyleSheetPool();
699 pStyleSheet = static_cast<SfxStyleSheet*>( pStylePool->Find( aNewStyleSheetName, pStyleSheet->GetFamily() ) );
700 }
701 }
702
703 // before we set the style sheet we need to preserve the bullet item
704 // since all items will be deleted while setting a new style sheet
705 SfxItemSet aOldAttrs( pOutliner->GetParaAttribs( nPara ) );
706
707 pOutliner->SetStyleSheet( nPara, pStyleSheet );
708
709 // restore the old bullet item but not if the style changed
710 if ( pOutliner->GetPrevDepth() != -1 && nDepth != -1 &&
711 aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET )
712 {
713 SfxItemSet aAttrs( pOutliner->GetParaAttribs( nPara ) );
714 aAttrs.Put( *aOldAttrs.GetItem( EE_PARA_NUMBULLET ) );
715 pOutliner->SetParaAttribs( nPara, aAttrs );
716 }
717 }
718
719 /**
720 * Handler for StatusEvents
721 */
IMPL_LINK_NOARG(OutlineView,StatusEventHdl,EditStatus &,void)722 IMPL_LINK_NOARG(OutlineView, StatusEventHdl, EditStatus&, void)
723 {
724 ::sd::Window* pWin = mrOutlineViewShell.GetActiveWindow();
725 OutlinerView* pOutlinerView = GetViewByWindow(pWin);
726 ::tools::Rectangle aVis = pOutlinerView->GetVisArea();
727 ::tools::Rectangle aText(Point(0,0),
728 Size(mnPaperWidth,
729 mrOutliner.GetTextHeight()));
730 ::tools::Rectangle aWin(Point(0,0), pWin->GetOutputSizePixel());
731 aWin = pWin->PixelToLogic(aWin);
732
733 if (!aVis.IsEmpty()) // not when opening
734 {
735 if (aWin.GetHeight() > aText.Bottom())
736 aText.SetBottom( aWin.GetHeight() );
737
738 mrOutlineViewShell.InitWindows(Point(0,0), aText.GetSize(), aVis.TopLeft());
739 mrOutlineViewShell.UpdateScrollBars();
740 }
741 }
742
IMPL_LINK_NOARG(OutlineView,BeginDropHdl,EditView *,void)743 IMPL_LINK_NOARG(OutlineView, BeginDropHdl, EditView*, void)
744 {
745 DBG_ASSERT(maDragAndDropModelGuard == nullptr,
746 "sd::OutlineView::BeginDropHdl(), prior drag operation not finished correctly!");
747
748 maDragAndDropModelGuard.reset( new OutlineViewModelChangeGuard( *this ) );
749 }
750
IMPL_LINK_NOARG(OutlineView,EndDropHdl,EditView *,void)751 IMPL_LINK_NOARG(OutlineView, EndDropHdl, EditView*, void)
752 {
753 maDragAndDropModelGuard.reset();
754 }
755
756 /**
757 * Handler for the start of a paragraph movement
758 */
IMPL_LINK(OutlineView,BeginMovingHdl,::Outliner *,pOutliner,void)759 IMPL_LINK( OutlineView, BeginMovingHdl, ::Outliner *, pOutliner, void )
760 {
761 OutlineViewPageChangesGuard aGuard(this);
762
763 // list of selected title paragraphs
764 mpOutlinerViews[0]->CreateSelectionList(maSelectedParas);
765
766 std::erase_if(maSelectedParas,
767 [](const Paragraph* pPara) { return !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE); });
768
769 // select the pages belonging to the paragraphs on level 0 to select
770 sal_uInt16 nPos = 0;
771 sal_Int32 nParaPos = 0;
772 Paragraph* pPara = pOutliner->GetParagraph( 0 );
773 std::vector<Paragraph*>::const_iterator fiter;
774
775 while(pPara)
776 {
777 if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) // one page?
778 {
779 maOldParaOrder.push_back(pPara);
780 SdPage* pPage = mrDoc.GetSdPage(nPos, PageKind::Standard);
781
782 fiter = std::find(maSelectedParas.begin(),maSelectedParas.end(),pPara);
783
784 pPage->SetSelected(fiter != maSelectedParas.end());
785
786 ++nPos;
787 }
788 pPara = pOutliner->GetParagraph( ++nParaPos );
789 }
790 }
791
792 /**
793 * Handler for the end of a paragraph movement
794 */
IMPL_LINK(OutlineView,EndMovingHdl,::Outliner *,pOutliner,void)795 IMPL_LINK( OutlineView, EndMovingHdl, ::Outliner *, pOutliner, void )
796 {
797 OutlineViewPageChangesGuard aGuard(this);
798
799 DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::EndMovingHdl(), model change without undo?!" );
800
801 // look for insertion position via the first paragraph
802 Paragraph* pSearchIt = maSelectedParas.empty() ? nullptr : *(maSelectedParas.begin());
803
804 // look for the first of the selected paragraphs in the new ordering
805 sal_uInt16 nPosNewOrder = 0;
806 sal_Int32 nParaPos = 0;
807 Paragraph* pPara = pOutliner->GetParagraph( 0 );
808 Paragraph* pPrev = nullptr;
809 while (pPara && pPara != pSearchIt)
810 {
811 if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) )
812 {
813 nPosNewOrder++;
814 pPrev = pPara;
815 }
816 pPara = pOutliner->GetParagraph( ++nParaPos );
817 }
818
819 sal_uInt16 nPos = nPosNewOrder; // don't change nPosNewOrder
820 if (nPos == 0)
821 {
822 nPos = sal_uInt16(-1); // insert before the first page
823 }
824 else
825 {
826 // look for the predecessor in the old ordering
827 std::vector<Paragraph*>::const_iterator it = std::find(maOldParaOrder.begin(),
828 maOldParaOrder.end(),
829 pPrev);
830
831 if (it != maOldParaOrder.end())
832 nPos = static_cast<sal_uInt16>(it-maOldParaOrder.begin());
833 else
834 nPos = 0xffff;
835
836 DBG_ASSERT(nPos != 0xffff, "Paragraph not found");
837 }
838
839 mrDoc.MovePages(nPos);
840
841 // deselect the pages again
842 sal_uInt16 nPageCount = static_cast<sal_uInt16>(maSelectedParas.size());
843 while (nPageCount)
844 {
845 SdPage* pPage = mrDoc.GetSdPage(nPosNewOrder, PageKind::Standard);
846 pPage->SetSelected(false);
847 nPosNewOrder++;
848 nPageCount--;
849 }
850
851 pOutliner->UpdateFields();
852
853 maSelectedParas.clear();
854 maOldParaOrder.clear();
855 }
856
857 /**
858 * Look for the title text object in one page of the model
859 */
GetTitleTextObject(SdrPage const * pPage)860 SdrTextObj* OutlineView::GetTitleTextObject(SdrPage const * pPage)
861 {
862 SdrTextObj* pResult = nullptr;
863
864 for (const rtl::Reference<SdrObject>& pObject : *pPage)
865 {
866 if (pObject->GetObjInventor() == SdrInventor::Default &&
867 pObject->GetObjIdentifier() == SdrObjKind::TitleText)
868 {
869 pResult = static_cast<SdrTextObj*>(pObject.get());
870 break;
871 }
872 }
873 return pResult;
874 }
875
876 /**
877 * Look for the outline text object in one page of the model
878 */
GetOutlineTextObject(SdrPage const * pPage)879 SdrTextObj* OutlineView::GetOutlineTextObject(SdrPage const * pPage)
880 {
881 SdrTextObj* pResult = nullptr;
882
883 for (const rtl::Reference<SdrObject>& pObject : *pPage)
884 {
885 if (pObject->GetObjInventor() == SdrInventor::Default &&
886 pObject->GetObjIdentifier() == SdrObjKind::OutlineText)
887 {
888 pResult = static_cast<SdrTextObj*>(pObject.get());
889 break;
890 }
891 }
892 return pResult;
893 }
894
CreateTitleTextObject(SdPage * pPage)895 SdrTextObj* OutlineView::CreateTitleTextObject(SdPage* pPage)
896 {
897 DBG_ASSERT( GetTitleTextObject(pPage) == nullptr, "sd::OutlineView::CreateTitleTextObject(), there is already a title text object!" );
898
899 if( pPage->GetAutoLayout() == AUTOLAYOUT_NONE )
900 {
901 // simple case
902 pPage->SetAutoLayout( AUTOLAYOUT_TITLE_ONLY, true );
903 }
904 else
905 {
906 // we already have a layout with a title but the title
907 // object was deleted, create a new one
908 pPage->InsertAutoLayoutShape( nullptr, PresObjKind::Title, false, pPage->GetTitleRect(), true );
909 }
910
911 return GetTitleTextObject(pPage);
912 }
913
CreateOutlineTextObject(SdPage * pPage)914 SdrTextObj* OutlineView::CreateOutlineTextObject(SdPage* pPage)
915 {
916 DBG_ASSERT( GetOutlineTextObject(pPage) == nullptr, "sd::OutlineView::CreateOutlineTextObject(), there is already a layout text object!" );
917
918 AutoLayout eNewLayout = pPage->GetAutoLayout();
919 switch( eNewLayout )
920 {
921 case AUTOLAYOUT_NONE:
922 case AUTOLAYOUT_TITLE_ONLY:
923 case AUTOLAYOUT_TITLE: eNewLayout = AUTOLAYOUT_TITLE_CONTENT; break;
924
925 case AUTOLAYOUT_CHART: eNewLayout = AUTOLAYOUT_CHARTTEXT; break;
926
927 case AUTOLAYOUT_ORG:
928 case AUTOLAYOUT_TAB:
929 case AUTOLAYOUT_OBJ: eNewLayout = AUTOLAYOUT_OBJTEXT; break;
930 default:
931 break;
932 }
933
934 if( eNewLayout != pPage->GetAutoLayout() )
935 {
936 pPage->SetAutoLayout( eNewLayout, true );
937 }
938 else
939 {
940 // we already have a layout with a text but the text
941 // object was deleted, create a new one
942 pPage->InsertAutoLayoutShape( nullptr,
943 PresObjKind::Outline,
944 false, pPage->GetLayoutRect(), true );
945 }
946
947 return GetOutlineTextObject(pPage);
948 }
949
950 /** updates draw model with all changes from outliner model */
PrepareClose()951 void OutlineView::PrepareClose()
952 {
953 ::sd::UndoManager* pDocUndoMgr = dynamic_cast<sd::UndoManager*>(mpDocSh->GetUndoManager());
954 if (pDocUndoMgr != nullptr)
955 pDocUndoMgr->SetLinkedUndoManager(nullptr);
956
957 mrOutliner.GetUndoManager().Clear();
958
959 BegUndo(SdResId(STR_UNDO_CHANGE_TITLE_AND_LAYOUT));
960 UpdateDocument();
961 EndUndo();
962 mrDoc.SetSelected(GetActualPage(), true);
963 }
964
965 /**
966 * Set attributes of the selected text
967 */
SetAttributes(const SfxItemSet & rSet,bool,bool,bool)968 bool OutlineView::SetAttributes(const SfxItemSet& rSet, bool /*bSlide*/, bool /*bReplaceAll*/, bool /*bMaster*/)
969 {
970 bool bOk = false;
971
972 OutlinerView* pOlView = GetViewByWindow(mrOutlineViewShell.GetActiveWindow());
973
974 if (pOlView)
975 {
976 pOlView->SetAttribs(rSet);
977 bOk = true;
978 }
979
980 mrOutlineViewShell.Invalidate (SID_PREVIEW_STATE);
981
982 return bOk;
983 }
984
985 /**
986 * Get attributes of the selected text
987 */
GetAttributes(SfxItemSet & rTargetSet,bool) const988 void OutlineView::GetAttributes( SfxItemSet& rTargetSet, bool ) const
989 {
990 OutlinerView* pOlView = GetViewByWindow(
991 mrOutlineViewShell.GetActiveWindow());
992 assert(pOlView && "No OutlinerView found");
993
994 rTargetSet.Put( pOlView->GetAttribs(), false );
995 }
996
997 /** creates outliner model from draw model */
FillOutliner()998 void OutlineView::FillOutliner()
999 {
1000 mrOutliner.GetUndoManager().Clear();
1001 mrOutliner.EnableUndo(false);
1002 ResetLinks();
1003 const bool bPrevUpdateLayout = mrOutliner.SetUpdateLayout(false);
1004
1005 Paragraph* pTitleToSelect = nullptr;
1006 sal_uInt16 nPageCount = mrDoc.GetSdPageCount(PageKind::Standard);
1007
1008 // fill outliner with paragraphs from slides title & (outlines|subtitles)
1009 for (sal_uInt16 nPage = 0; nPage < nPageCount; nPage++)
1010 {
1011 SdPage* pPage = mrDoc.GetSdPage(nPage, PageKind::Standard);
1012 Paragraph * pPara = nullptr;
1013
1014 // take text from title shape
1015 SdrTextObj* pTO = GetTitleTextObject(pPage);
1016 if(pTO && !(pTO->IsEmptyPresObj()))
1017 {
1018 OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject();
1019 if (pOPO)
1020 {
1021 bool bVertical = pOPO->IsEffectivelyVertical();
1022 pOPO->SetVertical( false );
1023 mrOutliner.AddText(*pOPO);
1024 pOPO->SetVertical( bVertical );
1025 pPara = mrOutliner.GetParagraph( mrOutliner.GetParagraphCount()-1 );
1026 }
1027 }
1028
1029 if( pPara == nullptr ) // no title, insert an empty paragraph
1030 {
1031 pPara = mrOutliner.Insert(OUString());
1032 mrOutliner.SetDepth(pPara, -1);
1033
1034 // do not apply hard attributes from the previous paragraph
1035 mrOutliner.SetParaAttribs( mrOutliner.GetAbsPos(pPara),
1036 mrOutliner.GetEmptyItemSet() );
1037
1038 mrOutliner.SetStyleSheet( mrOutliner.GetAbsPos( pPara ), pPage->GetStyleSheetForPresObj( PresObjKind::Title ) );
1039 }
1040
1041 mrOutliner.SetParaFlag( pPara, ParaFlag::ISPAGE );
1042
1043 sal_Int32 nPara = mrOutliner.GetAbsPos( pPara );
1044
1045 UpdateParagraph( nPara );
1046
1047 // remember paragraph of currently selected page
1048 if (pPage->IsSelected())
1049 pTitleToSelect = pPara;
1050
1051 // take text from subtitle or outline
1052 pTO = static_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Text));
1053 const bool bSubTitle = pTO != nullptr;
1054
1055 if (!pTO) // if no subtile found, try outline
1056 pTO = GetOutlineTextObject(pPage);
1057
1058 if(pTO && !(pTO->IsEmptyPresObj())) // found some text
1059 {
1060 OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject();
1061 if (pOPO)
1062 {
1063 sal_Int32 nParaCount1 = mrOutliner.GetParagraphCount();
1064 bool bVertical = pOPO->IsEffectivelyVertical();
1065 pOPO->SetVertical( false );
1066 mrOutliner.AddText(*pOPO);
1067 pOPO->SetVertical( bVertical );
1068
1069 sal_Int32 nParaCount2 = mrOutliner.GetParagraphCount();
1070 for (sal_Int32 n = nParaCount1; n < nParaCount2; n++)
1071 {
1072 if( bSubTitle )
1073 {
1074 Paragraph* p = mrOutliner.GetParagraph(n);
1075 if(p && mrOutliner.GetDepth( n ) > 0 )
1076 mrOutliner.SetDepth(p, 0);
1077 }
1078
1079 UpdateParagraph( n );
1080 }
1081 }
1082 }
1083 }
1084
1085 // place cursor at the start
1086 Paragraph* pFirstPara = mrOutliner.GetParagraph( 0 );
1087 mpOutlinerViews[0]->Select( pFirstPara );
1088 mpOutlinerViews[0]->Select( pFirstPara, false );
1089
1090 // select title of slide that was selected
1091 if (pTitleToSelect)
1092 mpOutlinerViews[0]->Select(pTitleToSelect);
1093
1094 SetLinks();
1095
1096 mrOutliner.EnableUndo(true);
1097
1098 mrOutliner.SetUpdateLayout(bPrevUpdateLayout);
1099 }
1100
1101 /**
1102 * Handler for deleting of level 0 paragraphs (pages): Warning
1103 */
IMPL_LINK_NOARG(OutlineView,RemovingPagesHdl,OutlinerView *,bool)1104 IMPL_LINK_NOARG(OutlineView, RemovingPagesHdl, OutlinerView*, bool)
1105 {
1106 sal_Int32 nNumOfPages = mrOutliner.GetSelPageCount();
1107
1108 if (nNumOfPages > PROCESS_WITH_PROGRESS_THRESHOLD)
1109 {
1110 mnPagesToProcess = nNumOfPages;
1111 mnPagesProcessed = 0;
1112 }
1113
1114 if (mnPagesToProcess)
1115 {
1116 mpProgress.reset( new SfxProgress( GetDocSh(), SdResId(STR_DELETE_PAGES), mnPagesToProcess ) );
1117 }
1118 mrOutliner.UpdateFields();
1119
1120 return true;
1121 }
1122
1123 /**
1124 * Handler for indenting level 0 paragraphs (pages): Warning
1125 */
IMPL_LINK(OutlineView,IndentingPagesHdl,OutlinerView *,pOutlinerView,bool)1126 IMPL_LINK( OutlineView, IndentingPagesHdl, OutlinerView *, pOutlinerView, bool )
1127 {
1128 return RemovingPagesHdl(pOutlinerView);
1129 }
1130
1131 /** returns the first slide that is selected in the outliner or where
1132 the cursor is located */
GetActualPage()1133 SdPage* OutlineView::GetActualPage()
1134 {
1135 ::sd::Window* pWin = mrOutlineViewShell.GetActiveWindow();
1136 OutlinerView* pActiveView = GetViewByWindow(pWin);
1137
1138 std::vector<Paragraph*> aSelList;
1139 pActiveView->CreateSelectionList(aSelList);
1140
1141 Paragraph *pPar = aSelList.empty() ? nullptr : *(aSelList.begin());
1142 SdPage* pCurrent = GetPageForParagraph(pPar);
1143
1144 DBG_ASSERT( pCurrent ||
1145 (mpDocSh->GetUndoManager() && static_cast< sd::UndoManager *>(mpDocSh->GetUndoManager())->IsDoing()) ||
1146 maDragAndDropModelGuard,
1147 "sd::OutlineView::GetActualPage(), no current page?" );
1148
1149 if( pCurrent )
1150 return pCurrent;
1151
1152 return mrDoc.GetSdPage( 0, PageKind::Standard );
1153 }
1154
GetPageForParagraph(Paragraph * pPara)1155 SdPage* OutlineView::GetPageForParagraph( Paragraph* pPara )
1156 {
1157 if( !::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) )
1158 pPara = GetPrevTitle(pPara);
1159
1160 sal_uInt32 nPageToSelect = 0;
1161 while(pPara)
1162 {
1163 pPara = GetPrevTitle(pPara);
1164 if(pPara)
1165 nPageToSelect++;
1166 }
1167
1168 if( nPageToSelect < static_cast<sal_uInt32>(mrDoc.GetSdPageCount( PageKind::Standard )) )
1169 return mrDoc.GetSdPage( static_cast<sal_uInt16>(nPageToSelect), PageKind::Standard );
1170
1171 return nullptr;
1172 }
1173
GetParagraphForPage(::Outliner const & rOutl,SdPage const * pPage)1174 Paragraph* OutlineView::GetParagraphForPage( ::Outliner const & rOutl, SdPage const * pPage )
1175 {
1176 // get the number of paragraphs with ident 0 we need to skip before
1177 // we find the actual page
1178 sal_uInt32 nPagesToSkip = (pPage->GetPageNum() - 1) >> 1;
1179
1180 sal_Int32 nParaPos = 0;
1181 Paragraph* pPara = rOutl.GetParagraph( 0 );
1182 while( pPara )
1183 {
1184 // if this paragraph is a page...
1185 if( ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) )
1186 {
1187 // see if we already skipped enough pages
1188 if( 0 == nPagesToSkip )
1189 break; // and if so, end the loop
1190
1191 // we skipped another page
1192 nPagesToSkip--;
1193 }
1194
1195 // get next paragraph
1196 pPara = mrOutliner.GetParagraph( ++nParaPos );
1197 }
1198
1199 return pPara;
1200 }
1201
1202 /** selects the paragraph for the given page at the outliner view*/
SetActualPage(SdPage const * pActual)1203 void OutlineView::SetActualPage( SdPage const * pActual )
1204 {
1205 if( pActual && mrOutliner.GetIgnoreCurrentPageChangesLevel()==0 && !mbFirstPaint)
1206 {
1207 // if we found a paragraph, select its text at the outliner view
1208 Paragraph* pPara = GetParagraphForPage( mrOutliner, pActual );
1209 if( pPara )
1210 mpOutlinerViews[0]->Select( pPara );
1211 }
1212 }
1213
1214 /**
1215 * Get StyleSheet from the selection
1216 */
GetStyleSheet() const1217 SfxStyleSheet* OutlineView::GetStyleSheet() const
1218 {
1219 ::sd::Window* pActWin = mrOutlineViewShell.GetActiveWindow();
1220 OutlinerView* pOlView = GetViewByWindow(pActWin);
1221 SfxStyleSheet* pResult = pOlView->GetStyleSheet();
1222 return pResult;
1223 }
1224
1225 /**
1226 * Mark pages as selected / not selected
1227 */
SetSelectedPages()1228 void OutlineView::SetSelectedPages()
1229 {
1230 // list of selected title paragraphs
1231 std::vector<Paragraph*> aSelParas;
1232 mpOutlinerViews[0]->CreateSelectionList(aSelParas);
1233
1234 std::erase_if(aSelParas,
1235 [](const Paragraph* pPara) { return !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE); });
1236
1237 // select the pages belonging to the paragraphs on level 0 to select
1238 sal_uInt16 nPos = 0;
1239 sal_Int32 nParaPos = 0;
1240 Paragraph *pPara = mrOutliner.GetParagraph( 0 );
1241 std::vector<Paragraph*>::const_iterator fiter;
1242
1243 while(pPara)
1244 {
1245 if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) // one page
1246 {
1247 SdPage* pPage = mrDoc.GetSdPage(nPos, PageKind::Standard);
1248 DBG_ASSERT(pPage!=nullptr,
1249 "Trying to select non-existing page OutlineView::SetSelectedPages()");
1250
1251 if (pPage)
1252 {
1253 fiter = std::find(aSelParas.begin(),aSelParas.end(),pPara);
1254 pPage->SetSelected(fiter != aSelParas.end());
1255 }
1256
1257 nPos++;
1258 }
1259
1260 pPara = mrOutliner.GetParagraph( ++nParaPos );
1261 }
1262 }
1263
1264 /**
1265 * Set new links
1266 */
SetLinks()1267 void OutlineView::SetLinks()
1268 {
1269 // set notification links
1270 mrOutliner.SetParaInsertedHdl(LINK(this, OutlineView, ParagraphInsertedHdl));
1271 mrOutliner.SetParaRemovingHdl(LINK(this, OutlineView, ParagraphRemovingHdl));
1272 mrOutliner.SetDepthChangedHdl(LINK(this, OutlineView, DepthChangedHdl));
1273 mrOutliner.SetBeginMovingHdl(LINK(this, OutlineView, BeginMovingHdl));
1274 mrOutliner.SetEndMovingHdl(LINK(this, OutlineView, EndMovingHdl));
1275 mrOutliner.SetRemovingPagesHdl(LINK(this, OutlineView, RemovingPagesHdl));
1276 mrOutliner.SetIndentingPagesHdl(LINK(this, OutlineView, IndentingPagesHdl));
1277 mrOutliner.SetStatusEventHdl(LINK(this, OutlineView, StatusEventHdl));
1278 mrOutliner.SetBeginDropHdl(LINK(this,OutlineView, BeginDropHdl));
1279 mrOutliner.SetEndDropHdl(LINK(this,OutlineView, EndDropHdl));
1280 mrOutliner.SetPaintFirstLineHdl(LINK(this,OutlineView,PaintingFirstLineHdl));
1281 mrOutliner.SetBeginPasteOrDropHdl(LINK(this,OutlineView, BeginPasteOrDropHdl));
1282 mrOutliner.SetEndPasteOrDropHdl(LINK(this,OutlineView, EndPasteOrDropHdl));
1283 }
1284
1285 /**
1286 * Restore old links
1287 */
ResetLinks() const1288 void OutlineView::ResetLinks() const
1289 {
1290 mrOutliner.SetParaInsertedHdl(Link<::Outliner::ParagraphHdlParam,void>());
1291 mrOutliner.SetParaRemovingHdl(Link<::Outliner::ParagraphHdlParam,void>());
1292 mrOutliner.SetDepthChangedHdl(Link<::Outliner::DepthChangeHdlParam,void>());
1293 mrOutliner.SetBeginMovingHdl(Link<::Outliner*,void>());
1294 mrOutliner.SetEndMovingHdl(Link<::Outliner*,void>());
1295 mrOutliner.SetStatusEventHdl(Link<EditStatus&,void>());
1296 mrOutliner.SetRemovingPagesHdl(Link<OutlinerView*,bool>());
1297 mrOutliner.SetIndentingPagesHdl(Link<OutlinerView*,bool>());
1298 mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
1299 mrOutliner.SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*,void>());
1300 mrOutliner.SetEndPasteOrDropHdl(Link<PasteOrDropInfos*,void>());
1301 }
1302
AcceptDrop(const AcceptDropEvent &,DropTargetHelper &,SdrLayerID)1303 sal_Int8 OutlineView::AcceptDrop( const AcceptDropEvent&, DropTargetHelper&, SdrLayerID)
1304 {
1305 return DND_ACTION_NONE;
1306 }
1307
ExecuteDrop(const ExecuteDropEvent &,::sd::Window *,sal_uInt16,SdrLayerID)1308 sal_Int8 OutlineView::ExecuteDrop( const ExecuteDropEvent&, ::sd::Window*, sal_uInt16, SdrLayerID)
1309 {
1310 return DND_ACTION_NONE;
1311 }
1312
1313 // Re-implement GetScriptType for this view to get correct results
GetScriptType() const1314 SvtScriptType OutlineView::GetScriptType() const
1315 {
1316 SvtScriptType nScriptType = ::sd::View::GetScriptType();
1317
1318 std::optional<OutlinerParaObject> pTempOPObj = mrOutliner.CreateParaObject();
1319 if(pTempOPObj)
1320 {
1321 nScriptType = pTempOPObj->GetTextObject().GetScriptType();
1322 }
1323
1324 return nScriptType;
1325 }
1326
onUpdateStyleSettings(bool bForceUpdate)1327 void OutlineView::onUpdateStyleSettings( bool bForceUpdate /* = false */ )
1328 {
1329 svtools::ColorConfig aColorConfig;
1330 const Color aDocColor( aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor );
1331 if( !(bForceUpdate || (maDocColor != aDocColor)) )
1332 return;
1333
1334 sal_uInt16 nView;
1335 for( nView = 0; nView < MAX_OUTLINERVIEWS; nView++ )
1336 {
1337 if (mpOutlinerViews[nView] != nullptr)
1338 {
1339 mpOutlinerViews[nView]->SetBackgroundColor( aDocColor );
1340
1341 vcl::Window* pWindow = mpOutlinerViews[nView]->GetWindow();
1342
1343 if( pWindow )
1344 pWindow->SetBackground( Wallpaper( aDocColor ) );
1345
1346 }
1347 }
1348
1349 mrOutliner.SetBackgroundColor( aDocColor );
1350
1351 maDocColor = aDocColor;
1352 }
1353
IMPL_LINK_NOARG(OutlineView,AppEventListenerHdl,VclSimpleEvent &,void)1354 IMPL_LINK_NOARG(OutlineView, AppEventListenerHdl, VclSimpleEvent&, void)
1355 {
1356 onUpdateStyleSettings(false);
1357 }
1358
IMPL_LINK(OutlineView,EventMultiplexerListener,::sd::tools::EventMultiplexerEvent &,rEvent,void)1359 IMPL_LINK(OutlineView, EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, rEvent, void)
1360 {
1361 switch (rEvent.meEventId)
1362 {
1363 case EventMultiplexerEventId::CurrentPageChanged:
1364 SetActualPage(mrOutlineViewShell.GetActualPage());
1365 break;
1366
1367 case EventMultiplexerEventId::PageOrder:
1368 if (mrOutliner.GetIgnoreCurrentPageChangesLevel()==0)
1369 {
1370 if (((mrDoc.GetPageCount()-1)%2) == 0)
1371 {
1372 mrOutliner.Clear();
1373 FillOutliner();
1374 ::sd::Window* pWindow = mrOutlineViewShell.GetActiveWindow();
1375 if (pWindow != nullptr)
1376 pWindow->Invalidate();
1377 }
1378 }
1379 break;
1380
1381 default: break;
1382 }
1383 }
1384
IgnoreCurrentPageChanges(bool bIgnoreChanges)1385 void OutlineView::IgnoreCurrentPageChanges (bool bIgnoreChanges)
1386 {
1387 if (bIgnoreChanges)
1388 mrOutliner.IncreIgnoreCurrentPageChangesLevel();
1389 else
1390 mrOutliner.DecreIgnoreCurrentPageChangesLevel();
1391 }
1392
1393 /** call this method before you do anything that can modify the outliner
1394 and or the drawing document model. It will create needed undo actions */
BeginModelChange()1395 void OutlineView::BeginModelChange()
1396 {
1397 mrOutliner.GetUndoManager().EnterListAction(u""_ustr, u""_ustr, 0, mrOutlineViewShell.GetViewShellBase().GetViewShellId());
1398 BegUndo(SdResId(STR_UNDO_CHANGE_TITLE_AND_LAYOUT));
1399 }
1400
1401 /** call this method after BeginModelChange(), when all possible model
1402 changes are done. */
EndModelChange()1403 void OutlineView::EndModelChange()
1404 {
1405 UpdateDocument();
1406
1407 SfxUndoManager* pDocUndoMgr = mpDocSh->GetUndoManager();
1408
1409 bool bHasUndoActions = pDocUndoMgr->GetUndoActionCount() != 0;
1410
1411 EndUndo();
1412
1413 DBG_ASSERT( bHasUndoActions == (mrOutliner.GetUndoManager().GetUndoActionCount() != 0), "sd::OutlineView::EndModelChange(), undo actions not in sync!" );
1414
1415 mrOutliner.GetUndoManager().LeaveListAction();
1416
1417 if( bHasUndoActions && mrOutliner.GetEditEngine().HasTriedMergeOnLastAddUndo() )
1418 TryToMergeUndoActions();
1419
1420 mrOutlineViewShell.Invalidate( SID_UNDO );
1421 mrOutlineViewShell.Invalidate( SID_REDO );
1422 }
1423
1424 /** updates all changes in the outliner model to the draw model */
UpdateDocument()1425 void OutlineView::UpdateDocument()
1426 {
1427 OutlineViewPageChangesGuard aGuard(this);
1428
1429 const sal_uInt32 nPageCount = mrDoc.GetSdPageCount(PageKind::Standard);
1430 Paragraph* pPara = mrOutliner.GetParagraph( 0 );
1431 sal_uInt32 nPage;
1432 for (nPage = 0; nPage < nPageCount; nPage++)
1433 {
1434 SdPage* pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Standard);
1435 mrDoc.SetSelected(pPage, false);
1436
1437 mrOutlineViewShell.UpdateTitleObject( pPage, pPara );
1438 mrOutlineViewShell.UpdateOutlineObject( pPage, pPara );
1439
1440 if( pPara )
1441 pPara = GetNextTitle(pPara);
1442 }
1443
1444 DBG_ASSERT( pPara == nullptr, "sd::OutlineView::UpdateDocument(), slides are out of sync, creating missing ones" );
1445 while( pPara )
1446 {
1447 SdPage* pPage = InsertSlideForParagraph( pPara );
1448 mrDoc.SetSelected(pPage, false);
1449
1450 mrOutlineViewShell.UpdateTitleObject( pPage, pPara );
1451 mrOutlineViewShell.UpdateOutlineObject( pPage, pPara );
1452
1453 pPara = GetNextTitle(pPara);
1454 }
1455 }
1456
1457 /** merge edit engine undo actions if possible */
TryToMergeUndoActions()1458 void OutlineView::TryToMergeUndoActions()
1459 {
1460 SfxUndoManager& rOutlineUndo = mrOutliner.GetUndoManager();
1461 if( rOutlineUndo.GetUndoActionCount() <= 1 )
1462 return;
1463
1464 SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction* >( rOutlineUndo.GetUndoAction() );
1465 SfxListUndoAction* pPrevListAction = dynamic_cast< SfxListUndoAction* >( rOutlineUndo.GetUndoAction(1) );
1466 if( !(pListAction && pPrevListAction) )
1467 return;
1468
1469 // find the top EditUndo action in the top undo action list
1470 size_t nAction = pListAction->maUndoActions.size();
1471 EditUndo* pEditUndo = nullptr;
1472 while( !pEditUndo && nAction )
1473 {
1474 pEditUndo = dynamic_cast< EditUndo* >(pListAction->GetUndoAction(--nAction));
1475 }
1476
1477 sal_uInt16 nEditPos = nAction; // we need this later to remove the merged undo actions
1478
1479 // make sure it is the only EditUndo action in the top undo list
1480 while( pEditUndo && nAction )
1481 {
1482 if( dynamic_cast< EditUndo* >(pListAction->GetUndoAction(--nAction)) )
1483 pEditUndo = nullptr;
1484 }
1485
1486 // do we have one and only one EditUndo action in the top undo list?
1487 if( !pEditUndo )
1488 return;
1489
1490 // yes, see if we can merge it with the prev undo list
1491
1492 nAction = pPrevListAction->maUndoActions.size();
1493 EditUndo* pPrevEditUndo = nullptr;
1494 while( !pPrevEditUndo && nAction )
1495 pPrevEditUndo = dynamic_cast< EditUndo* >(pPrevListAction->GetUndoAction(--nAction));
1496
1497 if( !(pPrevEditUndo && pPrevEditUndo->Merge( pEditUndo )) )
1498 return;
1499
1500 // ok we merged the only EditUndo of the top undo list with
1501 // the top EditUndo of the previous undo list
1502
1503 // first remove the merged undo action
1504 assert( pListAction->GetUndoAction(nEditPos) == pEditUndo &&
1505 "sd::OutlineView::TryToMergeUndoActions(), wrong edit pos!" );
1506 pListAction->Remove(nEditPos);
1507
1508 if ( !pListAction->maUndoActions.empty() )
1509 {
1510 // now we have to move all remaining doc undo actions from the top undo
1511 // list to the previous undo list and remove the top undo list
1512
1513 size_t nCount = pListAction->maUndoActions.size();
1514 size_t nDestAction = pPrevListAction->maUndoActions.size();
1515 while( nCount-- )
1516 {
1517 std::unique_ptr<SfxUndoAction> pTemp = pListAction->Remove(0);
1518 pPrevListAction->Insert( std::move(pTemp), nDestAction++ );
1519 }
1520 pPrevListAction->nCurUndoAction = pPrevListAction->maUndoActions.size();
1521 }
1522
1523 rOutlineUndo.RemoveLastUndoAction();
1524 }
1525
IMPL_LINK(OutlineView,PaintingFirstLineHdl,PaintFirstLineInfo *,pInfo,void)1526 IMPL_LINK(OutlineView, PaintingFirstLineHdl, PaintFirstLineInfo*, pInfo, void)
1527 {
1528 if( !pInfo )
1529 return;
1530
1531 Paragraph* pPara = mrOutliner.GetParagraph( pInfo->mnPara );
1532 EditEngine& rEditEngine = const_cast< EditEngine& >( mrOutliner.GetEditEngine() );
1533
1534 Size aImageSize( pInfo->mpOutDev->PixelToLogic( maSlideImage.GetSizePixel() ) );
1535 Size aOffset( 100, 100 );
1536
1537 // paint slide number
1538 if( !(pPara && ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE)) )
1539 return;
1540
1541 ::tools::Long nPage = 0; // todo, printing??
1542 for ( sal_Int32 n = 0; n <= pInfo->mnPara; n++ )
1543 {
1544 Paragraph* p = mrOutliner.GetParagraph( n );
1545 if ( ::Outliner::HasParaFlag(p,ParaFlag::ISPAGE) )
1546 nPage++;
1547 }
1548
1549 ::tools::Long nBulletHeight = static_cast<::tools::Long>(mrOutliner.GetLineHeight( pInfo->mnPara ));
1550 ::tools::Long nFontHeight = 0;
1551 if ( !rEditEngine.IsFlatMode() )
1552 {
1553 nFontHeight = nBulletHeight / 5;
1554 }
1555 else
1556 {
1557 nFontHeight = (nBulletHeight * 10) / 25;
1558 }
1559
1560 Size aFontSz( 0, nFontHeight );
1561
1562 Size aOutSize( 2000, nBulletHeight );
1563
1564 const float fImageHeight = (static_cast<float>(aOutSize.Height()) * float(4)) / float(7);
1565 if (aImageSize.Width() != 0)
1566 {
1567 const float fImageRatio = static_cast<float>(aImageSize.Height()) / static_cast<float>(aImageSize.Width());
1568 aImageSize.setWidth( static_cast<::tools::Long>( fImageRatio * fImageHeight ) );
1569 }
1570 aImageSize.setHeight( static_cast<::tools::Long>(fImageHeight) );
1571
1572 Point aImagePos( pInfo->mrStartPos );
1573 aImagePos.AdjustX(aOutSize.Width() - aImageSize.Width() - aOffset.Width() ) ;
1574 aImagePos.AdjustY((aOutSize.Height() - aImageSize.Height()) / 2 );
1575
1576 pInfo->mpOutDev->DrawImage( aImagePos, aImageSize, maSlideImage );
1577
1578 const bool bVertical = mrOutliner.IsVertical();
1579 const bool bRightToLeftPara = rEditEngine.IsRightToLeft( pInfo->mnPara );
1580
1581 LanguageType eLang = rEditEngine.GetDefaultLanguage();
1582
1583 Point aTextPos( aImagePos.X() - aOffset.Width(), pInfo->mrStartPos.Y() );
1584 vcl::Font aNewFont( OutputDevice::GetDefaultFont( DefaultFontType::SANS_UNICODE, eLang, GetDefaultFontFlags::NONE ) );
1585 aNewFont.SetFontSize( aFontSz );
1586 aNewFont.SetVertical( bVertical );
1587 aNewFont.SetOrientation( Degree10(bVertical ? 2700 : 0) );
1588 aNewFont.SetColor( COL_AUTO );
1589 pInfo->mpOutDev->SetFont( aNewFont );
1590 OUString aPageText = OUString::number( nPage );
1591 Size aTextSz;
1592 aTextSz.setWidth( pInfo->mpOutDev->GetTextWidth( aPageText ) );
1593 aTextSz.setHeight( pInfo->mpOutDev->GetTextHeight() );
1594 if ( !bVertical )
1595 {
1596 aTextPos.AdjustY((aOutSize.Height() - aTextSz.Height()) / 2 );
1597 if ( !bRightToLeftPara )
1598 {
1599 aTextPos.AdjustX( -(aTextSz.Width()) );
1600 }
1601 else
1602 {
1603 aTextPos.AdjustX(aTextSz.Width() );
1604 }
1605 }
1606 else
1607 {
1608 aTextPos.AdjustY( -(aTextSz.Width()) );
1609 aTextPos.AdjustX(nBulletHeight / 2 );
1610 }
1611 pInfo->mpOutDev->DrawText( aTextPos, aPageText );
1612 }
1613
UpdateParagraph(sal_Int32 nPara)1614 void OutlineView::UpdateParagraph( sal_Int32 nPara )
1615 {
1616 SfxItemSet aNewAttrs2( mrOutliner.GetParaAttribs( nPara ) );
1617 aNewAttrs2.Put( maLRSpaceItem );
1618 mrOutliner.SetParaAttribs( nPara, aNewAttrs2 );
1619 }
1620
OnBeginPasteOrDrop(PasteOrDropInfos *)1621 void OutlineView::OnBeginPasteOrDrop( PasteOrDropInfos* /*pInfo*/ )
1622 {
1623 }
1624
1625 /** this is called after a paste or drop operation, make sure that the newly inserted paragraphs
1626 get the correct style sheet and new slides are inserted. */
OnEndPasteOrDrop(PasteOrDropInfos * pInfo)1627 void OutlineView::OnEndPasteOrDrop( PasteOrDropInfos* pInfo )
1628 {
1629 SdPage* pPage = nullptr;
1630 SfxStyleSheetBasePool* pStylePool = GetDoc().GetStyleSheetPool();
1631
1632 for( sal_Int32 nPara = pInfo->nStartPara; nPara <= pInfo->nEndPara; nPara++ )
1633 {
1634 Paragraph* pPara = mrOutliner.GetParagraph( nPara );
1635
1636 bool bPage = ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE );
1637
1638 if( !bPage )
1639 {
1640 SdStyleSheet* pStyleSheet = dynamic_cast< SdStyleSheet* >( mrOutliner.GetStyleSheet( nPara ) );
1641 if( pStyleSheet )
1642 {
1643 if ( pStyleSheet->GetApiName() == "title" )
1644 bPage = true;
1645 }
1646 }
1647
1648 if( !pPara )
1649 continue; // fatality!?
1650
1651 if( bPage && (nPara != pInfo->nStartPara) )
1652 {
1653 // insert new slide for this paragraph
1654 pPage = InsertSlideForParagraph( pPara );
1655 }
1656 else
1657 {
1658 // newly inserted non page paragraphs get the outline style
1659 if( !pPage )
1660 pPage = GetPageForParagraph( pPara );
1661
1662 if( pPage )
1663 {
1664 SfxStyleSheet* pStyle = pPage->GetStyleSheetForPresObj( bPage ? PresObjKind::Title : PresObjKind::Outline );
1665
1666 if( !bPage )
1667 {
1668 const sal_Int16 nDepth = mrOutliner.GetDepth( nPara );
1669 if( nDepth > 0 )
1670 {
1671 OUString aStyleSheetName = pStyle->GetName();
1672 if (!aStyleSheetName.isEmpty())
1673 aStyleSheetName = aStyleSheetName.copy(0, aStyleSheetName.getLength() - 1);
1674 aStyleSheetName += OUString::number( nDepth );
1675 pStyle = static_cast<SfxStyleSheet*>( pStylePool->Find( aStyleSheetName, pStyle->GetFamily() ) );
1676 DBG_ASSERT( pStyle, "sd::OutlineView::OnEndPasteOrDrop(), Style not found!" );
1677 }
1678 }
1679
1680 mrOutliner.SetStyleSheet( nPara, pStyle );
1681 }
1682
1683 UpdateParagraph( nPara );
1684 }
1685 }
1686 }
1687
1688
OutlineViewModelChangeGuard(OutlineView & rView)1689 OutlineViewModelChangeGuard::OutlineViewModelChangeGuard( OutlineView& rView )
1690 : mrView( rView )
1691 {
1692 mrView.BeginModelChange();
1693 }
1694
~OutlineViewModelChangeGuard()1695 OutlineViewModelChangeGuard::~OutlineViewModelChangeGuard() COVERITY_NOEXCEPT_FALSE
1696 {
1697 mrView.EndModelChange();
1698 }
1699
1700
OutlineViewPageChangesGuard(OutlineView * pView)1701 OutlineViewPageChangesGuard::OutlineViewPageChangesGuard( OutlineView* pView )
1702 : mpView( pView )
1703 {
1704 if( mpView )
1705 mpView->IgnoreCurrentPageChanges( true );
1706 }
1707
~OutlineViewPageChangesGuard()1708 OutlineViewPageChangesGuard::~OutlineViewPageChangesGuard()
1709 {
1710 if( mpView )
1711 mpView->IgnoreCurrentPageChanges( false );
1712 }
1713
1714 } // end of namespace sd
1715
1716 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1717