Bump version to 24.04.3.4
[LibreOffice.git] / editeng / source / outliner / outliner.cxx
blobb6f715da52d0bfaf97fdcfb1d47f0f9ebd9a5d97
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/string.hxx>
21 #include <svl/eitem.hxx>
22 #include <svl/intitem.hxx>
23 #include <editeng/editeng.hxx>
24 #include <editeng/editview.hxx>
25 #include <editeng/editdata.hxx>
26 #include <editeng/lrspitem.hxx>
28 #include <math.h>
29 #include <svl/style.hxx>
30 #include <editeng/outliner.hxx>
31 #include "paralist.hxx"
32 #include <editeng/outlobj.hxx>
33 #include <outleeng.hxx>
34 #include "outlundo.hxx"
35 #include <editeng/eeitem.hxx>
36 #include <editeng/editstat.hxx>
37 #include <editeng/overflowingtxt.hxx>
38 #include <editeng/editobj.hxx>
39 #include <svl/itemset.hxx>
40 #include <vcl/metric.hxx>
41 #include <editeng/numitem.hxx>
42 #include <editeng/adjustitem.hxx>
43 #include <vcl/GraphicObject.hxx>
44 #include <editeng/svxfont.hxx>
45 #include <editeng/brushitem.hxx>
46 #include <svl/itempool.hxx>
47 #include <libxml/xmlwriter.h>
48 #include <sal/log.hxx>
49 #include <o3tl/safeint.hxx>
50 #include <o3tl/string_view.hxx>
51 #include <o3tl/temporary.hxx>
52 #include <osl/diagnose.h>
54 #include <memory>
55 using std::advance;
58 // Outliner
61 void Outliner::ImplCheckDepth( sal_Int16& rnDepth ) const
63 if( rnDepth < gnMinDepth )
64 rnDepth = gnMinDepth;
65 else if( rnDepth > nMaxDepth )
66 rnDepth = nMaxDepth;
69 Paragraph* Outliner::Insert(const OUString& rText, sal_Int32 nAbsPos, sal_Int16 nDepth)
71 DBG_ASSERT(pParaList->GetParagraphCount(),"Insert:No Paras");
73 Paragraph* pPara;
75 ImplCheckDepth( nDepth );
77 sal_Int32 nParagraphCount = pParaList->GetParagraphCount();
78 if( nAbsPos > nParagraphCount )
79 nAbsPos = nParagraphCount;
81 if( bFirstParaIsEmpty )
83 pPara = pParaList->GetParagraph( 0 );
84 if( pPara->GetDepth() != nDepth )
86 nDepthChangedHdlPrevDepth = pPara->GetDepth();
87 ParaFlag nPrevFlags = pPara->nFlags;
88 pPara->SetDepth( nDepth );
89 DepthChangedHdl(pPara, nPrevFlags);
91 pPara->nFlags |= ParaFlag::HOLDDEPTH;
92 SetText( rText, pPara );
94 else
96 bool bUpdate = pEditEngine->SetUpdateLayout( false );
97 ImplBlockInsertionCallbacks( true );
98 pPara = new Paragraph( nDepth );
99 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nAbsPos );
100 pEditEngine->InsertParagraph( nAbsPos, OUString() );
101 DBG_ASSERT(pPara==pParaList->GetParagraph(nAbsPos),"Insert:Failed");
102 ImplInitDepth( nAbsPos, nDepth, false );
103 ParagraphInsertedHdl(pPara);
104 pPara->nFlags |= ParaFlag::HOLDDEPTH;
105 SetText( rText, pPara );
106 ImplBlockInsertionCallbacks( false );
107 pEditEngine->SetUpdateLayout( bUpdate );
109 bFirstParaIsEmpty = false;
110 DBG_ASSERT(pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(),"SetText failed");
111 return pPara;
115 void Outliner::ParagraphInserted( sal_Int32 nPara )
118 if ( nBlockInsCallback )
119 return;
121 if( bPasting || pEditEngine->IsInUndo() )
123 Paragraph* pPara = new Paragraph( -1 );
124 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
125 if( pEditEngine->IsInUndo() )
127 pPara->bVisible = true;
128 const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
129 pPara->SetDepth( rLevel.GetValue() );
132 else
134 sal_Int16 nDepth = -1;
135 Paragraph* pParaBefore = pParaList->GetParagraph( nPara-1 );
136 if ( pParaBefore )
137 nDepth = pParaBefore->GetDepth();
139 Paragraph* pPara = new Paragraph( nDepth );
140 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
142 if( !pEditEngine->IsInUndo() )
144 ImplCalcBulletText( nPara, true, false );
145 ParagraphInsertedHdl(pPara);
150 void Outliner::ParagraphDeleted( sal_Int32 nPara )
153 if ( nBlockInsCallback || ( nPara == EE_PARA_ALL ) )
154 return;
156 Paragraph* pPara = pParaList->GetParagraph( nPara );
157 if (!pPara)
158 return;
160 sal_Int16 nDepth = pPara->GetDepth();
162 if( !pEditEngine->IsInUndo() )
164 aParaRemovingHdl.Call( { this, pPara } );
167 pParaList->Remove( nPara );
169 if( pEditEngine->IsInUndo() || bPasting )
170 return;
172 pPara = pParaList->GetParagraph( nPara );
173 if ( pPara && ( pPara->GetDepth() > nDepth ) )
175 ImplCalcBulletText( nPara, true, false );
176 // Search for next on the this level ...
177 while ( pPara && pPara->GetDepth() > nDepth )
178 pPara = pParaList->GetParagraph( ++nPara );
181 if ( pPara && ( pPara->GetDepth() == nDepth ) )
182 ImplCalcBulletText( nPara, true, false );
185 void Outliner::Init( OutlinerMode nMode )
187 nOutlinerMode = nMode;
189 Clear();
191 EEControlBits nCtrl = pEditEngine->GetControlWord();
192 nCtrl &= ~EEControlBits(EEControlBits::OUTLINER|EEControlBits::OUTLINER2);
194 SetMaxDepth( 9 );
196 switch ( GetOutlinerMode() )
198 case OutlinerMode::TextObject:
199 case OutlinerMode::TitleObject:
200 break;
202 case OutlinerMode::OutlineObject:
203 nCtrl |= EEControlBits::OUTLINER2;
204 break;
205 case OutlinerMode::OutlineView:
206 nCtrl |= EEControlBits::OUTLINER;
207 break;
209 default: OSL_FAIL( "Outliner::Init - Invalid Mode!" );
212 pEditEngine->SetControlWord( nCtrl );
214 const bool bWasUndoEnabled(IsUndoEnabled());
215 EnableUndo(false);
216 ImplInitDepth( 0, -1, false );
217 GetUndoManager().Clear();
218 EnableUndo(bWasUndoEnabled);
221 void Outliner::SetMaxDepth( sal_Int16 nDepth )
223 if( nMaxDepth != nDepth )
225 nMaxDepth = std::min( nDepth, sal_Int16(SVX_MAX_NUM-1) );
229 sal_Int16 Outliner::GetDepth( sal_Int32 nPara ) const
231 Paragraph* pPara = pParaList->GetParagraph( nPara );
232 DBG_ASSERT( pPara, "Outliner::GetDepth - Paragraph not found!" );
233 return pPara ? pPara->GetDepth() : -1;
236 void Outliner::SetDepth( Paragraph* pPara, sal_Int16 nNewDepth )
239 ImplCheckDepth( nNewDepth );
241 if ( nNewDepth == pPara->GetDepth() )
242 return;
244 nDepthChangedHdlPrevDepth = pPara->GetDepth();
245 ParaFlag nPrevFlags = pPara->nFlags;
247 sal_Int32 nPara = GetAbsPos( pPara );
248 ImplInitDepth( nPara, nNewDepth, true );
249 ImplCalcBulletText( nPara, false, false );
251 if ( GetOutlinerMode() == OutlinerMode::OutlineObject )
252 ImplSetLevelDependentStyleSheet( nPara );
254 DepthChangedHdl(pPara, nPrevFlags);
257 sal_Int16 Outliner::GetNumberingStartValue( sal_Int32 nPara ) const
259 Paragraph* pPara = pParaList->GetParagraph( nPara );
260 DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
261 return pPara ? pPara->GetNumberingStartValue() : -1;
264 void Outliner::SetNumberingStartValue( sal_Int32 nPara, sal_Int16 nNumberingStartValue )
266 Paragraph* pPara = pParaList->GetParagraph( nPara );
267 DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
268 if( pPara && pPara->GetNumberingStartValue() != nNumberingStartValue )
270 if( IsUndoEnabled() && !IsInUndo() )
271 InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
272 pPara->GetNumberingStartValue(), nNumberingStartValue,
273 pPara->IsParaIsNumberingRestart(), pPara->IsParaIsNumberingRestart() ) );
275 pPara->SetNumberingStartValue( nNumberingStartValue );
276 ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
277 pEditEngine->SetModified();
281 bool Outliner::IsParaIsNumberingRestart( sal_Int32 nPara ) const
283 Paragraph* pPara = pParaList->GetParagraph( nPara );
284 DBG_ASSERT( pPara, "Outliner::IsParaIsNumberingRestart - Paragraph not found!" );
285 return pPara && pPara->IsParaIsNumberingRestart();
288 void Outliner::SetParaIsNumberingRestart( sal_Int32 nPara, bool bParaIsNumberingRestart )
290 Paragraph* pPara = pParaList->GetParagraph( nPara );
291 DBG_ASSERT( pPara, "Outliner::SetParaIsNumberingRestart - Paragraph not found!" );
292 if( pPara && (pPara->IsParaIsNumberingRestart() != bParaIsNumberingRestart) )
294 if( IsUndoEnabled() && !IsInUndo() )
295 InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
296 pPara->GetNumberingStartValue(), pPara->GetNumberingStartValue(),
297 pPara->IsParaIsNumberingRestart(), bParaIsNumberingRestart ) );
299 pPara->SetParaIsNumberingRestart( bParaIsNumberingRestart );
300 ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
301 pEditEngine->SetModified();
305 sal_Int32 Outliner::GetBulletsNumberingStatus(
306 const sal_Int32 nParaStart,
307 const sal_Int32 nParaEnd ) const
309 if ( nParaStart > nParaEnd
310 || nParaEnd >= pParaList->GetParagraphCount() )
312 SAL_WARN("editeng", "<Outliner::GetBulletsNumberingStatus> - unexpected parameter values" );
313 return 2;
316 sal_Int32 nBulletsCount = 0;
317 sal_Int32 nNumberingCount = 0;
318 for (sal_Int32 nPara = nParaStart; nPara <= nParaEnd; ++nPara)
320 if ( !pParaList->GetParagraph(nPara) )
322 break;
324 const SvxNumberFormat* pFmt = GetNumberFormat(nPara);
325 if (!pFmt)
327 // At least, exists one paragraph that has no Bullets/Numbering.
328 break;
330 else if ((pFmt->GetNumberingType() == SVX_NUM_BITMAP) || (pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL))
332 // Having Bullets in this paragraph.
333 nBulletsCount++;
335 else
337 // Having Numbering in this paragraph.
338 nNumberingCount++;
342 const sal_Int32 nParaCount = nParaEnd - nParaStart + 1;
343 if ( nBulletsCount == nParaCount )
345 return 0;
347 else if ( nNumberingCount == nParaCount )
349 return 1;
351 return 2;
354 sal_Int32 Outliner::GetBulletsNumberingStatus() const
356 return pParaList->GetParagraphCount() > 0
357 ? GetBulletsNumberingStatus( 0, pParaList->GetParagraphCount()-1 )
358 : 2;
361 std::optional<OutlinerParaObject> Outliner::CreateParaObject( sal_Int32 nStartPara, sal_Int32 nCount ) const
363 if ( static_cast<sal_uInt64>(nStartPara) + nCount >
364 o3tl::make_unsigned(pParaList->GetParagraphCount()) )
365 nCount = pParaList->GetParagraphCount() - nStartPara;
367 // When a new OutlinerParaObject is created because a paragraph is just being deleted,
368 // it can happen that the ParaList is not updated yet...
369 if ( ( nStartPara + nCount ) > pEditEngine->GetParagraphCount() )
370 nCount = pEditEngine->GetParagraphCount() - nStartPara;
372 if (nCount <= 0)
373 return std::nullopt;
375 std::unique_ptr<EditTextObject> xText = pEditEngine->CreateTextObject( nStartPara, nCount );
376 const bool bIsEditDoc(OutlinerMode::TextObject == GetOutlinerMode());
377 ParagraphDataVector aParagraphDataVector(nCount);
378 const sal_Int32 nLastPara(nStartPara + nCount - 1);
380 for(sal_Int32 nPara(nStartPara); nPara <= nLastPara; nPara++)
382 aParagraphDataVector[nPara-nStartPara] = *GetParagraph(nPara);
385 xText->ClearPortionInfo(); // tdf#147166 the PortionInfo is unwanted here
386 OutlinerParaObject aPObj(std::move(xText), std::move(aParagraphDataVector), bIsEditDoc);
387 aPObj.SetOutlinerMode(GetOutlinerMode());
389 return aPObj;
392 void Outliner::SetToEmptyText()
394 SetText(GetEmptyParaObject());
397 void Outliner::SetText( const OUString& rText, Paragraph* pPara )
399 DBG_ASSERT(pPara,"SetText:No Para");
401 const sal_Int32 nPara = pParaList->GetAbsPos( pPara );
403 if (pEditEngine->GetText( nPara ) == rText)
405 // short-circuit logic to improve performance
406 bFirstParaIsEmpty = false;
407 return;
410 const bool bUpdate = pEditEngine->SetUpdateLayout( false );
411 ImplBlockInsertionCallbacks( true );
413 if (rText.isEmpty())
415 pEditEngine->SetText( nPara, rText );
416 ImplInitDepth( nPara, pPara->GetDepth(), false );
418 else
420 const OUString aText(convertLineEnd(rText, LINEEND_LF));
422 sal_Int32 nPos = 0;
423 sal_Int32 nInsPos = nPara+1;
424 sal_Int32 nIdx {0};
425 // Loop over all tokens, but ignore the last one if empty
426 // (i.e. if strings ends with the delimiter, detected by
427 // checking nIdx against string length). This check also
428 // handle empty strings.
429 while( nIdx>=0 && nIdx<aText.getLength() )
431 std::u16string_view aStr = o3tl::getToken(aText, 0, '\x0A', nIdx );
433 sal_Int16 nCurDepth;
434 if( nPos )
436 pPara = new Paragraph( -1 );
437 nCurDepth = -1;
439 else
440 nCurDepth = pPara->GetDepth();
442 // In the outliner mode, filter the tabs and set the indentation
443 // about a LRSpaceItem. In EditEngine mode intend over old tabs
444 if( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) ||
445 ( GetOutlinerMode() == OutlinerMode::OutlineView ) )
447 // Extract Tabs
448 size_t nTabs = 0;
449 while ( ( nTabs < aStr.size() ) && ( aStr[nTabs] == '\t' ) )
450 nTabs++;
451 if ( nTabs )
452 aStr = aStr.substr(nTabs);
454 // Keep depth? (see Outliner::Insert)
455 if( !(pPara->nFlags & ParaFlag::HOLDDEPTH) )
457 nCurDepth = nTabs-1; //TODO: sal_Int32 -> sal_Int16!
458 ImplCheckDepth( nCurDepth );
459 pPara->SetDepth( nCurDepth );
462 if( nPos ) // not with the first paragraph
464 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nInsPos );
465 pEditEngine->InsertParagraph( nInsPos, OUString(aStr) );
466 ParagraphInsertedHdl(pPara);
468 else
470 nInsPos--;
471 pEditEngine->SetText( nInsPos, OUString(aStr) );
473 ImplInitDepth( nInsPos, nCurDepth, false );
474 nInsPos++;
475 nPos++;
479 DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"SetText failed!");
480 bFirstParaIsEmpty = false;
481 ImplBlockInsertionCallbacks( false );
482 // Restore the update mode.
483 pEditEngine->SetUpdateLayout(bUpdate, /*bRestoring=*/true);
486 // pView == 0 -> Ignore tabs
488 bool Outliner::ImpConvertEdtToOut( sal_Int32 nPara )
491 bool bConverted = false;
492 sal_Int32 nTabs = 0;
493 ESelection aDelSel;
495 OUString aName;
497 OUString aStr( pEditEngine->GetText( nPara ) );
498 const sal_Unicode* pPtr = aStr.getStr();
500 sal_Int32 nHeadingNumberStart = 0;
501 sal_Int32 nNumberingNumberStart = 0;
502 SfxStyleSheet* pStyle= pEditEngine->GetStyleSheet( nPara );
503 if( pStyle )
505 OUString aHeading_US( "heading" );
506 OUString aNumber_US( "Numbering" );
507 aName = pStyle->GetName();
508 sal_Int32 nSearch;
509 if ( ( nSearch = aName.indexOf( aHeading_US ) ) != -1 )
510 nHeadingNumberStart = nSearch + aHeading_US.getLength();
511 else if ( ( nSearch = aName.indexOf( aNumber_US ) ) != -1 )
512 nNumberingNumberStart = nSearch + aNumber_US.getLength();
515 if ( nHeadingNumberStart || nNumberingNumberStart )
517 // PowerPoint import ?
518 if( nHeadingNumberStart && ( aStr.getLength() >= 2 ) &&
519 ( pPtr[0] != '\t' ) && ( pPtr[1] == '\t' ) )
521 // Extract Bullet and Tab
522 aDelSel = ESelection( nPara, 0, nPara, 2 );
525 sal_Int32 nPos = nHeadingNumberStart ? nHeadingNumberStart : nNumberingNumberStart;
526 std::u16string_view aLevel = comphelper::string::stripStart(aName.subView(nPos), ' ');
527 nTabs = o3tl::toInt32(aLevel);
528 if( nTabs )
529 nTabs--; // Level 0 = "heading 1"
530 bConverted = true;
532 else
534 // filter leading tabs
535 while( *pPtr == '\t' )
537 pPtr++;
538 nTabs++;
540 // Remove tabs from the text
541 if( nTabs )
542 aDelSel = ESelection( nPara, 0, nPara, nTabs );
545 if ( aDelSel.HasRange() )
547 pEditEngine->QuickDelete( aDelSel );
550 const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
551 sal_Int16 nOutlLevel = rLevel.GetValue();
553 ImplCheckDepth( nOutlLevel );
554 ImplInitDepth( nPara, nOutlLevel, false );
556 return bConverted;
559 void Outliner::SetText( const OutlinerParaObject& rPObj )
561 bool bUpdate = pEditEngine->SetUpdateLayout( false );
563 bool bUndo = pEditEngine->IsUndoEnabled();
564 EnableUndo( false );
566 Init( rPObj.GetOutlinerMode() );
568 ImplBlockInsertionCallbacks( true );
569 pEditEngine->SetText(rPObj.GetTextObject());
571 bFirstParaIsEmpty = false;
573 pParaList->Clear();
574 for( sal_Int32 nCurPara = 0; nCurPara < rPObj.Count(); nCurPara++ )
576 std::unique_ptr<Paragraph> pPara(new Paragraph( rPObj.GetParagraphData(nCurPara)));
577 ImplCheckDepth( pPara->nDepth );
579 pParaList->Append(std::move(pPara));
580 ImplCheckNumBulletItem( nCurPara );
583 ImplCheckParagraphs( 0, pParaList->GetParagraphCount() );
585 EnableUndo( bUndo );
586 ImplBlockInsertionCallbacks( false );
587 pEditEngine->SetUpdateLayout( bUpdate );
589 DBG_ASSERT( pParaList->GetParagraphCount()==rPObj.Count(),"SetText failed");
590 DBG_ASSERT( pEditEngine->GetParagraphCount()==rPObj.Count(),"SetText failed");
593 void Outliner::AddText( const OutlinerParaObject& rPObj, bool bAppend )
595 bool bUpdate = pEditEngine->SetUpdateLayout( false );
597 ImplBlockInsertionCallbacks( true );
598 sal_Int32 nPara;
599 if( bFirstParaIsEmpty )
601 pParaList->Clear();
602 pEditEngine->SetText(rPObj.GetTextObject());
603 nPara = 0;
604 bAppend = false;
606 else
608 nPara = pParaList->GetParagraphCount();
609 pEditEngine->InsertParagraph( EE_PARA_APPEND, rPObj.GetTextObject(), bAppend );
611 bFirstParaIsEmpty = false;
613 for( sal_Int32 n = 0; n < rPObj.Count(); n++ )
615 if ( n == 0 && bAppend )
617 // This first "paragraph" was just appended to an existing (incomplete) paragraph.
618 // Since no new paragraph will be added, the assumed increase-by-1 also won't happen.
619 --nPara;
620 continue;
623 Paragraph* pPara = new Paragraph( rPObj.GetParagraphData(n) );
624 pParaList->Append(std::unique_ptr<Paragraph>(pPara));
625 sal_Int32 nP = nPara+n;
626 DBG_ASSERT(pParaList->GetAbsPos(pPara)==nP,"AddText:Out of sync");
627 ImplInitDepth( nP, pPara->GetDepth(), false );
629 DBG_ASSERT( pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(), "SetText: OutOfSync" );
631 ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
633 ImplBlockInsertionCallbacks( false );
634 pEditEngine->SetUpdateLayout( bUpdate );
637 OUString Outliner::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
639 if ( !aCalcFieldValueHdl.IsSet() )
640 return OUString( ' ' );
642 EditFieldInfo aFldInfo( this, rField, nPara, nPos );
643 // The FldColor is preset with COL_LIGHTGRAY.
644 if ( rpFldColor )
645 aFldInfo.SetFieldColor( *rpFldColor );
647 aCalcFieldValueHdl.Call( &aFldInfo );
648 if ( aFldInfo.GetTextColor() )
650 rpTxtColor = *aFldInfo.GetTextColor();
653 if ( aFldInfo.GetFontLineStyle() )
655 rpFldLineStyle = *aFldInfo.GetFontLineStyle();
658 if (aFldInfo.GetFieldColor())
659 rpFldColor = *aFldInfo.GetFieldColor();
660 else
661 rpFldColor.reset();
663 return aFldInfo.GetRepresentation();
666 void Outliner::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
668 Paragraph* pPara = pParaList->GetParagraph( nPara );
669 if (pPara)
671 pEditEngine->SetStyleSheet( nPara, pStyle );
672 ImplCheckNumBulletItem( nPara );
676 void Outliner::ImplCheckNumBulletItem( sal_Int32 nPara )
678 Paragraph* pPara = pParaList->GetParagraph( nPara );
679 if (pPara)
680 pPara->aBulSize.setWidth( -1 );
683 void Outliner::ImplSetLevelDependentStyleSheet( sal_Int32 nPara )
686 DBG_ASSERT( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) || ( GetOutlinerMode() == OutlinerMode::OutlineView ), "SetLevelDependentStyleSheet: Wrong Mode!" );
688 SfxStyleSheet* pStyle = GetStyleSheet( nPara );
690 if ( !pStyle )
691 return;
693 sal_Int16 nDepth = GetDepth( nPara );
694 if( nDepth < 0 )
695 nDepth = 0;
697 OUString aNewStyleSheetName( pStyle->GetName() );
698 aNewStyleSheetName = aNewStyleSheetName.subView( 0, aNewStyleSheetName.getLength()-1 ) +
699 OUString::number( nDepth+1 );
700 SfxStyleSheet* pNewStyle = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( aNewStyleSheetName, pStyle->GetFamily() ));
701 DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" );
702 if ( pNewStyle && ( pNewStyle != GetStyleSheet( nPara ) ) )
704 SfxItemSet aOldAttrs( GetParaAttribs( nPara ) );
705 SetStyleSheet( nPara, pNewStyle );
706 if ( aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET )
708 SfxItemSet aAttrs( GetParaAttribs( nPara ) );
709 aAttrs.Put( aOldAttrs.Get( EE_PARA_NUMBULLET ) );
710 SetParaAttribs( nPara, aAttrs );
715 void Outliner::ImplInitDepth( sal_Int32 nPara, sal_Int16 nDepth, bool bCreateUndo )
718 DBG_ASSERT( ( nDepth >= gnMinDepth ) && ( nDepth <= nMaxDepth ), "ImplInitDepth - Depth is invalid!" );
720 Paragraph* pPara = pParaList->GetParagraph( nPara );
721 if (!pPara)
722 return;
723 sal_Int16 nOldDepth = pPara->GetDepth();
724 pPara->SetDepth( nDepth );
726 // For IsInUndo attributes and style do not have to be set, there
727 // the old values are restored by the EditEngine.
728 if( IsInUndo() )
729 return;
731 bool bUpdate = pEditEngine->SetUpdateLayout( false );
733 bool bUndo = bCreateUndo && IsUndoEnabled();
735 SfxItemSet aAttrs( pEditEngine->GetParaAttribs( nPara ) );
736 aAttrs.Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nDepth ) );
737 pEditEngine->SetParaAttribs( nPara, aAttrs );
738 ImplCheckNumBulletItem( nPara );
739 ImplCalcBulletText( nPara, false, false );
741 if ( bUndo )
743 InsertUndo( std::make_unique<OutlinerUndoChangeDepth>( this, nPara, nOldDepth, nDepth ) );
746 pEditEngine->SetUpdateLayout( bUpdate );
749 void Outliner::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
752 pEditEngine->SetParaAttribs( nPara, rSet );
755 void Outliner::SetCharAttribs(sal_Int32 nPara, const SfxItemSet& rSet)
757 pEditEngine->SetCharAttribs(nPara, rSet);
760 bool Outliner::Expand( Paragraph const * pPara )
762 if ( !pParaList->HasHiddenChildren( pPara ) )
763 return false;
765 std::unique_ptr<OLUndoExpand> pUndo;
766 bool bUndo = IsUndoEnabled() && !IsInUndo();
767 if( bUndo )
769 UndoActionStart( OLUNDO_EXPAND );
770 pUndo.reset( new OLUndoExpand( this, OLUNDO_EXPAND ) );
771 pUndo->nCount = pParaList->GetAbsPos( pPara );
773 pParaList->Expand( pPara );
774 InvalidateBullet(pParaList->GetAbsPos(pPara));
775 if( bUndo )
777 InsertUndo( std::move(pUndo) );
778 UndoActionEnd();
780 return true;
783 bool Outliner::Collapse( Paragraph const * pPara )
785 if ( !pParaList->HasVisibleChildren( pPara ) ) // collapsed
786 return false;
788 std::unique_ptr<OLUndoExpand> pUndo;
789 bool bUndo = false;
791 if( !IsInUndo() && IsUndoEnabled() )
792 bUndo = true;
793 if( bUndo )
795 UndoActionStart( OLUNDO_COLLAPSE );
796 pUndo.reset( new OLUndoExpand( this, OLUNDO_COLLAPSE ) );
797 pUndo->nCount = pParaList->GetAbsPos( pPara );
800 pParaList->Collapse( pPara );
801 InvalidateBullet(pParaList->GetAbsPos(pPara));
802 if( bUndo )
804 InsertUndo( std::move(pUndo) );
805 UndoActionEnd();
807 return true;
810 vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) const
812 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
813 DBG_ASSERT( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ), "ImpCalcBulletFont: Missing or BitmapBullet!" );
815 vcl::Font aStdFont;
816 if ( !pEditEngine->IsFlatMode() )
818 ESelection aSel( nPara, 0, nPara, 0 );
819 aStdFont = EditEngine::CreateFontFromItemSet( pEditEngine->GetAttribs( aSel ), pEditEngine->GetScriptType( aSel ) );
821 else
823 aStdFont = pEditEngine->GetStandardFont( nPara );
826 vcl::Font aBulletFont;
827 std::optional<vcl::Font> pSourceFont;
828 if ( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
830 pSourceFont = pFmt->GetBulletFont();
833 if (pSourceFont)
835 aBulletFont = *pSourceFont;
837 else
839 aBulletFont = aStdFont;
840 aBulletFont.SetUnderline( LINESTYLE_NONE );
841 aBulletFont.SetOverline( LINESTYLE_NONE );
842 aBulletFont.SetStrikeout( STRIKEOUT_NONE );
843 aBulletFont.SetEmphasisMark( FontEmphasisMark::NONE );
844 aBulletFont.SetRelief( FontRelief::NONE );
847 // Use original scale...
849 double fFontScaleY = pFmt->GetBulletRelSize() * (getScalingParameters().fFontY / 100.0);
850 double fScaledLineHeight = aStdFont.GetFontSize().Height();
851 fScaledLineHeight *= fFontScaleY * 10;
852 fScaledLineHeight /= 1000.0;
854 aBulletFont.SetAlignment( ALIGN_BOTTOM );
855 aBulletFont.SetFontSize(Size(0, basegfx::fround(fScaledLineHeight)));
856 bool bVertical = IsVertical();
857 aBulletFont.SetVertical( bVertical );
858 aBulletFont.SetOrientation( Degree10(bVertical ? (IsTopToBottom() ? 2700 : 900) : 0) );
860 Color aColor( COL_AUTO );
861 if( !pEditEngine->IsFlatMode() && !( pEditEngine->GetControlWord() & EEControlBits::NOCOLORS ) )
863 aColor = pFmt->GetBulletColor();
866 if ( ( aColor == COL_AUTO ) || ( IsForceAutoColor() ) )
867 aColor = pEditEngine->GetAutoColor();
869 aBulletFont.SetColor( aColor );
870 return aBulletFont;
873 void Outliner::PaintBullet(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin,
874 Degree10 nOrientation, OutputDevice& rOutDev)
877 bool bDrawBullet = false;
878 if (pEditEngine)
880 const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
881 bDrawBullet = rBulletState.GetValue();
884 if (!(bDrawBullet && ImplHasNumberFormat(nPara)))
885 return;
887 bool bVertical = IsVertical();
888 bool bTopToBottom = IsTopToBottom();
890 bool bRightToLeftPara = pEditEngine->IsRightToLeft( nPara );
892 tools::Rectangle aBulletArea( ImpCalcBulletArea( nPara, true, false ) );
894 double fSpacingFactorX = getScalingParameters().fSpacingX / 100.0;
896 tools::Long nStretchBulletX = basegfx::fround(double(aBulletArea.Left()) * fSpacingFactorX);
897 tools::Long nStretchBulletWidth = basegfx::fround(double(aBulletArea.GetWidth()) * fSpacingFactorX);
898 aBulletArea = tools::Rectangle(Point(nStretchBulletX, aBulletArea.Top()),
899 Size(nStretchBulletWidth, aBulletArea.GetHeight()) );
901 Paragraph* pPara = pParaList->GetParagraph( nPara );
902 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
903 if ( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) )
905 if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
907 vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
908 // Use baseline
909 bool bSymbol = pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL;
910 aBulletFont.SetAlignment( bSymbol ? ALIGN_BOTTOM : ALIGN_BASELINE );
911 vcl::Font aOldFont = rOutDev.GetFont();
912 rOutDev.SetFont( aBulletFont );
914 ParagraphInfos aParaInfos = pEditEngine->GetParagraphInfos( nPara );
915 Point aTextPos;
916 if ( !bVertical )
918 // aTextPos.Y() = rStartPos.Y() + aBulletArea.Bottom();
919 aTextPos.setY( rStartPos.Y() + ( bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent ) );
920 if ( !bRightToLeftPara )
921 aTextPos.setX( rStartPos.X() + aBulletArea.Left() );
922 else
923 aTextPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
925 else
927 if (bTopToBottom)
929 // aTextPos.X() = rStartPos.X() - aBulletArea.Bottom();
930 aTextPos.setX( rStartPos.X() - (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
931 aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
933 else
935 aTextPos.setX( rStartPos.X() + (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
936 aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
940 if ( nOrientation )
942 // Both TopLeft and bottom left is not quite correct,
943 // since in EditEngine baseline ...
944 rOrigin.RotateAround(aTextPos, nOrientation);
946 vcl::Font aRotatedFont( aBulletFont );
947 aRotatedFont.SetOrientation( nOrientation );
948 rOutDev.SetFont( aRotatedFont );
951 // VCL will take care of brackets and so on...
952 vcl::text::ComplexTextLayoutFlags nLayoutMode = rOutDev.GetLayoutMode();
953 nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags(vcl::text::ComplexTextLayoutFlags::BiDiRtl|vcl::text::ComplexTextLayoutFlags::BiDiStrong);
954 if ( bRightToLeftPara )
955 nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
956 rOutDev.SetLayoutMode( nLayoutMode );
958 if(bStrippingPortions)
960 const vcl::Font& aSvxFont(rOutDev.GetFont());
961 KernArray aBuf;
962 rOutDev.GetTextArray( pPara->GetText(), &aBuf );
964 if(bSymbol)
966 // aTextPos is Bottom, go to Baseline
967 FontMetric aMetric(rOutDev.GetFontMetric());
968 aTextPos.AdjustY( -(aMetric.GetDescent()) );
971 assert(aBuf.get_factor() == 1);
972 DrawingText(aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf.get_subunit_array(), {},
973 aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color());
975 else
977 rOutDev.DrawText( aTextPos, pPara->GetText() );
980 rOutDev.SetFont( aOldFont );
982 else
984 if ( pFmt->GetBrush()->GetGraphicObject() )
986 Point aBulletPos;
987 if ( !bVertical )
989 aBulletPos.setY( rStartPos.Y() + aBulletArea.Top() );
990 if ( !bRightToLeftPara )
991 aBulletPos.setX( rStartPos.X() + aBulletArea.Left() );
992 else
993 aBulletPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
995 else
997 if (bTopToBottom)
999 aBulletPos.setX( rStartPos.X() - aBulletArea.Bottom() );
1000 aBulletPos.setY( rStartPos.Y() + aBulletArea.Left() );
1002 else
1004 aBulletPos.setX( rStartPos.X() + aBulletArea.Top() );
1005 aBulletPos.setY( rStartPos.Y() - aBulletArea.Right() );
1009 if(bStrippingPortions)
1011 if(aDrawBulletHdl.IsSet())
1013 // call something analog to aDrawPortionHdl (if set) and feed it something
1014 // analog to DrawPortionInfo...
1015 // created aDrawBulletHdl, Set/GetDrawBulletHdl.
1016 // created DrawBulletInfo and added handling to sdrtextdecomposition.cxx
1017 DrawBulletInfo aDrawBulletInfo(
1018 *pFmt->GetBrush()->GetGraphicObject(),
1019 aBulletPos,
1020 pPara->aBulSize);
1022 aDrawBulletHdl.Call(&aDrawBulletInfo);
1025 else
1027 pFmt->GetBrush()->GetGraphicObject()->Draw(rOutDev, aBulletPos, pPara->aBulSize);
1033 // In case of collapsed subparagraphs paint a line before the text.
1034 if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) ||
1035 bStrippingPortions || nOrientation )
1036 return;
1038 tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width();
1040 Point aStartPos, aEndPos;
1041 if ( !bVertical )
1043 aStartPos.setY( rStartPos.Y() + aBulletArea.Bottom() );
1044 if ( !bRightToLeftPara )
1045 aStartPos.setX( rStartPos.X() + aBulletArea.Right() );
1046 else
1047 aStartPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Left() );
1048 aEndPos = aStartPos;
1049 aEndPos.AdjustX(nWidth );
1051 else
1053 aStartPos.setX( rStartPos.X() - aBulletArea.Bottom() );
1054 aStartPos.setY( rStartPos.Y() + aBulletArea.Right() );
1055 aEndPos = aStartPos;
1056 aEndPos.AdjustY(nWidth );
1059 const Color& rOldLineColor = rOutDev.GetLineColor();
1060 rOutDev.SetLineColor( COL_BLACK );
1061 rOutDev.DrawLine( aStartPos, aEndPos );
1062 rOutDev.SetLineColor( rOldLineColor );
1065 void Outliner::InvalidateBullet(sal_Int32 nPara)
1067 tools::Long nLineHeight = static_cast<tools::Long>(pEditEngine->GetLineHeight(nPara ));
1068 for (OutlinerView* pView : aViewList)
1070 Point aPos( pView->pEditView->GetWindowPosTopLeft(nPara ) );
1071 tools::Rectangle aRect( pView->GetOutputArea() );
1072 aRect.SetRight( aPos.X() );
1073 aRect.SetTop( aPos.Y() );
1074 aRect.SetBottom( aPos.Y() );
1075 aRect.AdjustBottom(nLineHeight );
1077 pView->pEditView->InvalidateWindow(aRect);
1081 ErrCode Outliner::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
1084 bool bOldUndo = pEditEngine->IsUndoEnabled();
1085 EnableUndo( false );
1087 bool bUpdate = pEditEngine->SetUpdateLayout( false );
1089 Clear();
1091 ImplBlockInsertionCallbacks( true );
1092 ErrCode nRet = pEditEngine->Read( rInput, rBaseURL, eFormat, pHTTPHeaderAttrs );
1094 bFirstParaIsEmpty = false;
1096 sal_Int32 nParas = pEditEngine->GetParagraphCount();
1097 pParaList->Clear();
1098 for ( sal_Int32 n = 0; n < nParas; n++ )
1100 std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
1101 pParaList->Append(std::move(pPara));
1104 ImpFilterIndents( 0, nParas-1 );
1106 ImplBlockInsertionCallbacks( false );
1107 pEditEngine->SetUpdateLayout( bUpdate );
1108 EnableUndo( bOldUndo );
1110 return nRet;
1114 void Outliner::ImpFilterIndents( sal_Int32 nFirstPara, sal_Int32 nLastPara )
1116 bool bUpdate = pEditEngine->SetUpdateLayout( false );
1118 Paragraph* pLastConverted = nullptr;
1119 for( sal_Int32 nPara = nFirstPara; nPara <= nLastPara; nPara++ )
1121 Paragraph* pPara = pParaList->GetParagraph( nPara );
1122 if (pPara)
1124 if( ImpConvertEdtToOut( nPara ) )
1126 pLastConverted = pPara;
1128 else if ( pLastConverted )
1130 // Arrange normal paragraphs below the heading ...
1131 pPara->SetDepth( pLastConverted->GetDepth() );
1134 ImplInitDepth( nPara, pPara->GetDepth(), false );
1138 pEditEngine->SetUpdateLayout( bUpdate );
1141 EditUndoManager& Outliner::GetUndoManager()
1143 return pEditEngine->GetUndoManager();
1146 EditUndoManager* Outliner::SetUndoManager(EditUndoManager* pNew)
1148 return pEditEngine->SetUndoManager(pNew);
1151 void Outliner::ImpTextPasted( sal_Int32 nStartPara, sal_Int32 nCount )
1153 bool bUpdate = pEditEngine->SetUpdateLayout( false );
1155 const sal_Int32 nStart = nStartPara;
1157 Paragraph* pPara = pParaList->GetParagraph( nStartPara );
1159 while( nCount && pPara )
1161 if( GetOutlinerMode() != OutlinerMode::TextObject )
1163 nDepthChangedHdlPrevDepth = pPara->GetDepth();
1164 ParaFlag nPrevFlags = pPara->nFlags;
1166 ImpConvertEdtToOut( nStartPara );
1168 if( nStartPara == nStart )
1170 // the existing paragraph has changed depth or flags
1171 if( (pPara->GetDepth() != nDepthChangedHdlPrevDepth) || (pPara->nFlags != nPrevFlags) )
1172 DepthChangedHdl(pPara, nPrevFlags);
1175 else // EditEngine mode
1177 sal_Int16 nDepth = -1;
1178 const SfxItemSet& rAttrs = pEditEngine->GetParaAttribs( nStartPara );
1179 if ( rAttrs.GetItemState( EE_PARA_OUTLLEVEL ) == SfxItemState::SET )
1181 const SfxInt16Item& rLevel = rAttrs.Get( EE_PARA_OUTLLEVEL );
1182 nDepth = rLevel.GetValue();
1184 if ( nDepth != GetDepth( nStartPara ) )
1185 ImplInitDepth( nStartPara, nDepth, false );
1188 nCount--;
1189 nStartPara++;
1190 pPara = pParaList->GetParagraph( nStartPara );
1193 pEditEngine->SetUpdateLayout( bUpdate );
1195 DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"ImpTextPasted failed");
1198 bool Outliner::IndentingPagesHdl( OutlinerView* pView )
1200 if( !aIndentingPagesHdl.IsSet() )
1201 return true;
1202 return aIndentingPagesHdl.Call( pView );
1205 bool Outliner::ImpCanIndentSelectedPages( OutlinerView* pCurView )
1207 // The selected pages must already be set in advance through
1208 // ImpCalcSelectedPages
1210 // If the first paragraph is on level 0 it can not indented in any case,
1211 // possible there might be indentations in the following on the 0 level.
1212 if ( ( mnFirstSelPage == 0 ) && ( GetOutlinerMode() != OutlinerMode::TextObject ) )
1214 if ( nDepthChangedHdlPrevDepth == 1 ) // is the only page
1215 return false;
1216 else
1217 (void)pCurView->ImpCalcSelectedPages( false ); // without the first
1219 return IndentingPagesHdl( pCurView );
1223 bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView )
1225 // The selected pages must already be set in advance through
1226 // ImpCalcSelectedPages
1227 return RemovingPagesHdl( pCurView );
1230 Outliner::Outliner(SfxItemPool* pPool, OutlinerMode nMode)
1231 : mnFirstSelPage(0)
1232 , nDepthChangedHdlPrevDepth(0)
1233 , nMaxDepth(9)
1234 , bFirstParaIsEmpty(true)
1235 , nBlockInsCallback(0)
1236 , bStrippingPortions(false)
1237 , bPasting(false)
1240 pParaList.reset( new ParagraphList );
1241 pParaList->SetVisibleStateChangedHdl( LINK( this, Outliner, ParaVisibleStateChangedHdl ) );
1242 std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
1243 pParaList->Append(std::move(pPara));
1245 pEditEngine.reset( new OutlinerEditEng( this, pPool ) );
1246 pEditEngine->SetBeginMovingParagraphsHdl( LINK( this, Outliner, BeginMovingParagraphsHdl ) );
1247 pEditEngine->SetEndMovingParagraphsHdl( LINK( this, Outliner, EndMovingParagraphsHdl ) );
1248 pEditEngine->SetBeginPasteOrDropHdl( LINK( this, Outliner, BeginPasteOrDropHdl ) );
1249 pEditEngine->SetEndPasteOrDropHdl( LINK( this, Outliner, EndPasteOrDropHdl ) );
1251 Init( nMode );
1254 Outliner::~Outliner()
1256 pParaList->Clear();
1257 pParaList.reset();
1258 pEditEngine.reset();
1261 size_t Outliner::InsertView( OutlinerView* pView, size_t nIndex )
1263 size_t ActualIndex;
1265 if ( nIndex >= aViewList.size() )
1267 aViewList.push_back( pView );
1268 ActualIndex = aViewList.size() - 1;
1270 else
1272 ViewList::iterator it = aViewList.begin();
1273 advance( it, nIndex );
1274 ActualIndex = nIndex;
1276 pEditEngine->InsertView( pView->pEditView.get(), nIndex );
1277 return ActualIndex;
1280 void Outliner::RemoveView( OutlinerView const * pView )
1282 ViewList::iterator it = std::find(aViewList.begin(), aViewList.end(), pView);
1283 if (it != aViewList.end())
1285 pView->pEditView->HideCursor(); // HACK
1286 pEditEngine->RemoveView( pView->pEditView.get() );
1287 aViewList.erase( it );
1291 void Outliner::RemoveView( size_t nIndex )
1293 EditView* pEditView = pEditEngine->GetView( nIndex );
1294 pEditView->HideCursor(); // HACK
1296 pEditEngine->RemoveView( nIndex );
1299 ViewList::iterator it = aViewList.begin();
1300 advance( it, nIndex );
1301 aViewList.erase( it );
1306 OutlinerView* Outliner::GetView( size_t nIndex ) const
1308 return ( nIndex >= aViewList.size() ) ? nullptr : aViewList[ nIndex ];
1311 size_t Outliner::GetViewCount() const
1313 return aViewList.size();
1316 void Outliner::ParagraphInsertedHdl(Paragraph* pPara)
1318 if( !IsInUndo() )
1319 aParaInsertedHdl.Call( { this, pPara } );
1323 void Outliner::DepthChangedHdl(Paragraph* pPara, ParaFlag nPrevFlags)
1325 if( !IsInUndo() )
1326 aDepthChangedHdl.Call( { this, pPara, nPrevFlags } );
1330 sal_Int32 Outliner::GetAbsPos( Paragraph const * pPara ) const
1332 DBG_ASSERT(pPara,"GetAbsPos:No Para");
1333 return pParaList->GetAbsPos( pPara );
1336 sal_Int32 Outliner::GetParagraphCount() const
1338 return pParaList->GetParagraphCount();
1341 Paragraph* Outliner::GetParagraph( sal_Int32 nAbsPos ) const
1343 return pParaList->GetParagraph( nAbsPos );
1346 bool Outliner::HasChildren( Paragraph const * pParagraph ) const
1348 return pParaList->HasChildren( pParagraph );
1351 bool Outliner::ImplHasNumberFormat( sal_Int32 nPara ) const
1353 return GetNumberFormat(nPara) != nullptr;
1356 const SvxNumberFormat* Outliner::GetNumberFormat( sal_Int32 nPara ) const
1358 const SvxNumberFormat* pFmt = nullptr;
1360 Paragraph* pPara = pParaList->GetParagraph( nPara );
1361 if (!pPara)
1362 return nullptr;
1364 sal_Int16 nDepth = pPara->GetDepth();
1366 if( nDepth >= 0 )
1368 const SvxNumBulletItem& rNumBullet = pEditEngine->GetParaAttrib( nPara, EE_PARA_NUMBULLET );
1369 if ( rNumBullet.GetNumRule().GetLevelCount() > nDepth )
1370 pFmt = rNumBullet.GetNumRule().Get( nDepth );
1373 return pFmt;
1376 Size Outliner::ImplGetBulletSize( sal_Int32 nPara )
1378 Paragraph* pPara = pParaList->GetParagraph( nPara );
1379 if (!pPara)
1380 return Size();
1382 if( pPara->aBulSize.Width() == -1 )
1384 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1385 DBG_ASSERT( pFmt, "ImplGetBulletSize - no Bullet!" );
1387 if ( pFmt->GetNumberingType() == SVX_NUM_NUMBER_NONE )
1389 pPara->aBulSize = Size( 0, 0 );
1391 else if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
1393 OUString aBulletText = ImplGetBulletText( nPara );
1394 OutputDevice* pRefDev = pEditEngine->GetRefDevice();
1395 vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
1396 vcl::Font aRefFont( pRefDev->GetFont());
1397 pRefDev->SetFont( aBulletFont );
1398 pPara->aBulSize.setWidth( pRefDev->GetTextWidth( aBulletText ) );
1399 pPara->aBulSize.setHeight( pRefDev->GetTextHeight() );
1400 pRefDev->SetFont( aRefFont );
1402 else
1404 pPara->aBulSize = OutputDevice::LogicToLogic(pFmt->GetGraphicSize(),
1405 MapMode(MapUnit::Map100thMM),
1406 pEditEngine->GetRefDevice()->GetMapMode());
1410 return pPara->aBulSize;
1413 void Outliner::ImplCheckParagraphs( sal_Int32 nStart, sal_Int32 nEnd )
1416 for ( sal_Int32 n = nStart; n < nEnd; n++ )
1418 Paragraph* pPara = pParaList->GetParagraph( n );
1419 if (pPara)
1421 pPara->Invalidate();
1422 ImplCalcBulletText( n, false, false );
1427 void Outliner::SetRefDevice( OutputDevice* pRefDev )
1429 pEditEngine->SetRefDevice( pRefDev );
1430 for ( sal_Int32 n = pParaList->GetParagraphCount(); n; )
1432 Paragraph* pPara = pParaList->GetParagraph( --n );
1433 pPara->Invalidate();
1437 void Outliner::ParaAttribsChanged( sal_Int32 nPara )
1439 // The Outliner does not have an undo of its own, when paragraphs are
1440 // separated/merged. When ParagraphInserted the attribute EE_PARA_OUTLLEVEL
1441 // may not be set, this is however needed when the depth of the paragraph
1442 // is to be determined.
1443 if (!pEditEngine->IsInUndo())
1444 return;
1445 if (pParaList->GetParagraphCount() != pEditEngine->GetParagraphCount())
1446 return;
1447 Paragraph* pPara = pParaList->GetParagraph(nPara);
1448 if (!pPara)
1449 return;
1450 // tdf#100734: force update of bullet
1451 pPara->Invalidate();
1452 const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
1453 if (pPara->GetDepth() == rLevel.GetValue())
1454 return;
1455 pPara->SetDepth(rLevel.GetValue());
1456 ImplCalcBulletText(nPara, true, true);
1459 void Outliner::StyleSheetChanged( SfxStyleSheet const * pStyle )
1462 // The EditEngine calls StyleSheetChanged also for derived styles.
1463 // Here all the paragraphs, which had the said template, used to be
1464 // hunted by an ImpRecalcParaAttribs, why?
1465 // => only the Bullet-representation can really change...
1466 sal_Int32 nParas = pParaList->GetParagraphCount();
1467 for( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
1469 if ( pEditEngine->GetStyleSheet( nPara ) == pStyle )
1471 ImplCheckNumBulletItem( nPara );
1472 ImplCalcBulletText( nPara, false, false );
1473 // EditEngine formats changed paragraphs before calling this method,
1474 // so they are not reformatted now and use wrong bullet indent
1475 pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
1480 tools::Rectangle Outliner::ImpCalcBulletArea( sal_Int32 nPara, bool bAdjust, bool bReturnPaperPos )
1482 // Bullet area within the paragraph ...
1483 tools::Rectangle aBulletArea;
1485 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1486 if ( pFmt )
1488 Point aTopLeft;
1489 Size aBulletSize( ImplGetBulletSize( nPara ) );
1491 bool bOutlineMode = bool( pEditEngine->GetControlWord() & EEControlBits::OUTLINER );
1493 // the ODF attribute text:space-before which holds the spacing to add to the left of the label
1494 const auto nSpaceBefore = pFmt->GetAbsLSpace() + pFmt->GetFirstLineOffset();
1496 const SvxLRSpaceItem& rLR = pEditEngine->GetParaAttrib( nPara, bOutlineMode ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
1497 aTopLeft.setX( rLR.GetTextLeft() + rLR.GetTextFirstLineOffset() + nSpaceBefore );
1499 tools::Long nBulletWidth = std::max( static_cast<tools::Long>(-rLR.GetTextFirstLineOffset()), static_cast<tools::Long>((-pFmt->GetFirstLineOffset()) + pFmt->GetCharTextDistance()) );
1500 if ( nBulletWidth < aBulletSize.Width() ) // The Bullet creates its space
1501 nBulletWidth = aBulletSize.Width();
1503 if ( bAdjust && !bOutlineMode )
1505 // Adjust when centered or align right
1506 const SvxAdjustItem& rItem = pEditEngine->GetParaAttrib( nPara, EE_PARA_JUST );
1507 if ( ( !pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Left ) ) ||
1508 ( pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Right ) ) )
1510 aTopLeft.setX( pEditEngine->GetFirstLineStartX( nPara ) - nBulletWidth );
1514 // Vertical:
1515 ParagraphInfos aInfos = pEditEngine->GetParagraphInfos( nPara );
1516 if ( aInfos.bValid )
1518 aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintBullet) from the EditEngine
1519 aInfos.nFirstLineHeight - aInfos.nFirstLineTextHeight
1520 + aInfos.nFirstLineTextHeight / 2
1521 - aBulletSize.Height() / 2 );
1522 // may prefer to print out on the baseline ...
1523 if( ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
1525 vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
1526 if ( aBulletFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL )
1528 OutputDevice* pRefDev = pEditEngine->GetRefDevice();
1529 vcl::Font aOldFont = pRefDev->GetFont();
1530 pRefDev->SetFont( aBulletFont );
1531 FontMetric aMetric( pRefDev->GetFontMetric() );
1532 // Leading on the first line ...
1533 aTopLeft.setY( /* aInfos.nFirstLineOffset + */ aInfos.nFirstLineMaxAscent );
1534 aTopLeft.AdjustY( -(aMetric.GetAscent()) );
1535 pRefDev->SetFont( aOldFont );
1540 // Horizontal:
1541 if( pFmt->GetNumAdjust() == SvxAdjust::Right )
1543 aTopLeft.AdjustX(nBulletWidth - aBulletSize.Width() );
1545 else if( pFmt->GetNumAdjust() == SvxAdjust::Center )
1547 aTopLeft.AdjustX(( nBulletWidth - aBulletSize.Width() ) / 2 );
1550 if ( aTopLeft.X() < 0 ) // then push
1551 aTopLeft.setX( 0 );
1553 aBulletArea = tools::Rectangle( aTopLeft, aBulletSize );
1555 if ( bReturnPaperPos )
1557 Size aBulletSize( aBulletArea.GetSize() );
1558 Point aBulletDocPos( aBulletArea.TopLeft() );
1559 aBulletDocPos.AdjustY(pEditEngine->GetDocPosTopLeft( nPara ).Y() );
1560 Point aBulletPos( aBulletDocPos );
1562 if ( IsVertical() )
1564 aBulletPos.setY( aBulletDocPos.X() );
1565 aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.Y() );
1566 // Rotate:
1567 aBulletPos.AdjustX( -(aBulletSize.Height()) );
1568 Size aSz( aBulletSize );
1569 aBulletSize.setWidth( aSz.Height() );
1570 aBulletSize.setHeight( aSz.Width() );
1572 else if ( pEditEngine->IsRightToLeft( nPara ) )
1574 aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.X() - aBulletSize.Width() );
1577 aBulletArea = tools::Rectangle( aBulletPos, aBulletSize );
1579 return aBulletArea;
1582 EBulletInfo Outliner::GetBulletInfo( sal_Int32 nPara )
1584 EBulletInfo aInfo;
1586 aInfo.nParagraph = nPara;
1587 aInfo.bVisible = ImplHasNumberFormat( nPara );
1589 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1590 aInfo.nType = pFmt ? pFmt->GetNumberingType() : 0;
1592 if( pFmt )
1594 if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
1596 aInfo.aText = ImplGetBulletText( nPara );
1598 if( pFmt->GetBulletFont() )
1599 aInfo.aFont = *pFmt->GetBulletFont();
1603 if ( aInfo.bVisible )
1605 aInfo.aBounds = ImpCalcBulletArea( nPara, true, true );
1608 return aInfo;
1611 OUString Outliner::GetText( Paragraph const * pParagraph, sal_Int32 nCount ) const
1614 OUStringBuffer aText(128);
1615 sal_Int32 nStartPara = pParaList->GetAbsPos( pParagraph );
1616 for ( sal_Int32 n = 0; n < nCount; n++ )
1618 aText.append(pEditEngine->GetText( nStartPara + n ));
1619 if ( (n+1) < nCount )
1620 aText.append("\n");
1622 return aText.makeStringAndClear();
1625 void Outliner::Remove( Paragraph const * pPara, sal_Int32 nParaCount )
1628 sal_Int32 nPos = pParaList->GetAbsPos( pPara );
1629 if( !nPos && ( nParaCount >= pParaList->GetParagraphCount() ) )
1631 Clear();
1633 else
1635 for( sal_Int32 n = 0; n < nParaCount; n++ )
1636 pEditEngine->RemoveParagraph( nPos );
1640 void Outliner::StripPortions()
1642 bStrippingPortions = true;
1643 pEditEngine->StripPortions();
1644 bStrippingPortions = false;
1647 void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart,
1648 sal_Int32 nTextLen, std::span<const sal_Int32> pDXArray,
1649 std::span<const sal_Bool> pKashidaArray, const SvxFont& rFont,
1650 sal_Int32 nPara, sal_uInt8 nRightToLeft,
1651 const EEngineData::WrongSpellVector* pWrongSpellVector,
1652 const SvxFieldData* pFieldData,
1653 bool bEndOfLine,
1654 bool bEndOfParagraph,
1655 bool bEndOfBullet,
1656 const css::lang::Locale* pLocale,
1657 const Color& rOverlineColor,
1658 const Color& rTextLineColor)
1660 if(aDrawPortionHdl.IsSet())
1662 DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, nPara, pDXArray, pKashidaArray, pWrongSpellVector,
1663 pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft, false, 0, bEndOfLine, bEndOfParagraph, bEndOfBullet);
1665 aDrawPortionHdl.Call( &aInfo );
1669 void Outliner::DrawingTab( const Point& rStartPos, tools::Long nWidth, const OUString& rChar, const SvxFont& rFont,
1670 sal_Int32 nPara, sal_uInt8 nRightToLeft, bool bEndOfLine, bool bEndOfParagraph,
1671 const Color& rOverlineColor, const Color& rTextLineColor)
1673 if(aDrawPortionHdl.IsSet())
1675 DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, nPara, {}, {}, nullptr,
1676 nullptr, nullptr, rOverlineColor, rTextLineColor, nRightToLeft, true, nWidth, bEndOfLine, bEndOfParagraph, false);
1678 aDrawPortionHdl.Call( &aInfo );
1682 bool Outliner::RemovingPagesHdl( OutlinerView* pView )
1684 return !aRemovingPagesHdl.IsSet() || aRemovingPagesHdl.Call( pView );
1687 bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView, sal_Int32 _nFirstPage, sal_Int32 nPages )
1690 nDepthChangedHdlPrevDepth = nPages;
1691 mnFirstSelPage = _nFirstPage;
1692 return RemovingPagesHdl( pCurView );
1695 SfxItemSet const & Outliner::GetParaAttribs( sal_Int32 nPara ) const
1697 return pEditEngine->GetParaAttribs( nPara );
1700 IMPL_LINK( Outliner, ParaVisibleStateChangedHdl, Paragraph&, rPara, void )
1702 sal_Int32 nPara = pParaList->GetAbsPos( &rPara );
1703 pEditEngine->ShowParagraph( nPara, rPara.IsVisible() );
1706 IMPL_LINK_NOARG(Outliner, BeginMovingParagraphsHdl, MoveParagraphsInfo&, void)
1708 if( !IsInUndo() )
1709 aBeginMovingHdl.Call( this );
1712 IMPL_LINK( Outliner, BeginPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
1714 UndoActionStart( EDITUNDO_DRAGANDDROP );
1715 maBeginPasteOrDropHdl.Call(&rInfos);
1718 IMPL_LINK( Outliner, EndPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
1720 bPasting = false;
1721 ImpTextPasted( rInfos.nStartPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
1722 maEndPasteOrDropHdl.Call( &rInfos );
1723 UndoActionEnd();
1726 IMPL_LINK( Outliner, EndMovingParagraphsHdl, MoveParagraphsInfo&, rInfos, void )
1728 pParaList->MoveParagraphs( rInfos.nStartPara, rInfos.nDestPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
1729 sal_Int32 nChangesStart = std::min( rInfos.nStartPara, rInfos.nDestPara );
1730 sal_Int32 nParas = pParaList->GetParagraphCount();
1731 for ( sal_Int32 n = nChangesStart; n < nParas; n++ )
1732 ImplCalcBulletText( n, false, false );
1734 if( !IsInUndo() )
1735 aEndMovingHdl.Call( this );
1738 static bool isSameNumbering( const SvxNumberFormat& rN1, const SvxNumberFormat& rN2 )
1740 if( rN1.GetNumberingType() != rN2.GetNumberingType() )
1741 return false;
1743 if( rN1.GetNumStr(1) != rN2.GetNumStr(1) )
1744 return false;
1746 if( (rN1.GetPrefix() != rN2.GetPrefix()) || (rN1.GetSuffix() != rN2.GetSuffix()) )
1747 return false;
1749 return true;
1752 sal_uInt16 Outliner::ImplGetNumbering( sal_Int32 nPara, const SvxNumberFormat* pParaFmt )
1754 sal_uInt16 nNumber = pParaFmt->GetStart() - 1;
1756 Paragraph* pPara = pParaList->GetParagraph( nPara );
1757 const sal_Int16 nParaDepth = pPara->GetDepth();
1761 pPara = pParaList->GetParagraph( nPara );
1762 const sal_Int16 nDepth = pPara->GetDepth();
1764 // ignore paragraphs that are below our paragraph or have no numbering
1765 if( (nDepth > nParaDepth) || (nDepth == -1) )
1766 continue;
1768 // stop on paragraphs that are above our paragraph
1769 if( nDepth < nParaDepth )
1770 break;
1772 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1774 if( pFmt == nullptr )
1775 continue; // ignore paragraphs without bullets
1777 // check if numbering less than or equal to pParaFmt
1778 if( !isSameNumbering( *pFmt, *pParaFmt ) || ( pFmt->GetStart() < pParaFmt->GetStart() ) )
1779 break;
1781 if ( pFmt->GetStart() > pParaFmt->GetStart() )
1783 nNumber += pFmt->GetStart() - pParaFmt->GetStart();
1784 pParaFmt = pFmt;
1787 const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
1789 if( rBulletState.GetValue() )
1790 nNumber += 1;
1792 // same depth, same number format, check for restart
1793 const sal_Int16 nNumberingStartValue = pPara->GetNumberingStartValue();
1794 if( (nNumberingStartValue != -1) || pPara->IsParaIsNumberingRestart() )
1796 if( nNumberingStartValue != -1 )
1797 nNumber += nNumberingStartValue - 1;
1798 break;
1801 while( nPara-- );
1803 return nNumber;
1806 void Outliner::ImplCalcBulletText( sal_Int32 nPara, bool bRecalcLevel, bool bRecalcChildren )
1809 Paragraph* pPara = pParaList->GetParagraph( nPara );
1811 while ( pPara )
1813 OUString aBulletText;
1814 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1815 if( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) )
1817 aBulletText += pFmt->GetPrefix();
1818 if( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
1820 sal_UCS4 cChar = pFmt->GetBulletChar();
1821 aBulletText += OUString(&cChar, 1);
1823 else if( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE )
1825 aBulletText += pFmt->GetNumStr( ImplGetNumbering( nPara, pFmt ) );
1827 aBulletText += pFmt->GetSuffix();
1830 if (pPara->GetText() != aBulletText)
1831 pPara->SetText( aBulletText );
1833 if ( bRecalcLevel )
1835 sal_Int16 nDepth = pPara->GetDepth();
1836 pPara = pParaList->GetParagraph( ++nPara );
1837 if ( !bRecalcChildren )
1839 while ( pPara && ( pPara->GetDepth() > nDepth ) )
1840 pPara = pParaList->GetParagraph( ++nPara );
1843 if ( pPara && ( pPara->GetDepth() < nDepth ) )
1844 pPara = nullptr;
1846 else
1848 pPara = nullptr;
1853 void Outliner::Clear()
1856 if( !bFirstParaIsEmpty )
1858 ImplBlockInsertionCallbacks( true );
1859 pEditEngine->Clear();
1860 pParaList->Clear();
1861 pParaList->Append( std::unique_ptr<Paragraph>(new Paragraph( gnMinDepth )));
1862 bFirstParaIsEmpty = true;
1863 ImplBlockInsertionCallbacks( false );
1865 else
1867 Paragraph* pPara = pParaList->GetParagraph( 0 );
1868 if(pPara)
1869 pPara->SetDepth( gnMinDepth );
1873 void Outliner::SetFlatMode( bool bFlat )
1876 if( bFlat != pEditEngine->IsFlatMode() )
1878 for ( sal_Int32 nPara = pParaList->GetParagraphCount(); nPara; )
1879 pParaList->GetParagraph( --nPara )->aBulSize.setWidth( -1 );
1881 pEditEngine->SetFlatMode( bFlat );
1885 OUString Outliner::ImplGetBulletText( sal_Int32 nPara )
1887 OUString aRes;
1888 Paragraph* pPara = pParaList->GetParagraph( nPara );
1889 if (pPara)
1891 ImplCalcBulletText( nPara, false, false );
1892 aRes = pPara->GetText();
1894 return aRes;
1897 // this is needed for StarOffice Api
1898 void Outliner::SetLevelDependentStyleSheet( sal_Int32 nPara )
1900 SfxItemSet aOldAttrs( pEditEngine->GetParaAttribs( nPara ) );
1901 ImplSetLevelDependentStyleSheet( nPara );
1902 pEditEngine->SetParaAttribs( nPara, aOldAttrs );
1905 void Outliner::ImplBlockInsertionCallbacks( bool b )
1907 if ( b )
1909 nBlockInsCallback++;
1911 else
1913 DBG_ASSERT( nBlockInsCallback, "ImplBlockInsertionCallbacks ?!" );
1914 nBlockInsCallback--;
1915 if ( !nBlockInsCallback )
1917 // Call blocked notify events...
1918 while(!pEditEngine->aNotifyCache.empty())
1920 EENotify aNotify(pEditEngine->aNotifyCache.front());
1921 // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
1922 pEditEngine->aNotifyCache.erase(pEditEngine->aNotifyCache.begin());
1923 pEditEngine->aOutlinerNotifyHdl.Call( aNotify );
1929 IMPL_LINK( Outliner, EditEngineNotifyHdl, EENotify&, rNotify, void )
1931 if ( !nBlockInsCallback )
1932 pEditEngine->aOutlinerNotifyHdl.Call( rNotify );
1933 else
1934 pEditEngine->aNotifyCache.push_back(rNotify);
1937 /** sets a link that is called at the beginning of a drag operation at an edit view */
1938 void Outliner::SetBeginDropHdl( const Link<EditView*,void>& rLink )
1940 pEditEngine->SetBeginDropHdl( rLink );
1943 /** sets a link that is called at the end of a drag operation at an edit view */
1944 void Outliner::SetEndDropHdl( const Link<EditView*,void>& rLink )
1946 pEditEngine->SetEndDropHdl( rLink );
1949 /** sets a link that is called before a drop or paste operation. */
1950 void Outliner::SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
1952 maBeginPasteOrDropHdl = rLink;
1955 /** sets a link that is called after a drop or paste operation. */
1956 void Outliner::SetEndPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
1958 maEndPasteOrDropHdl = rLink;
1961 void Outliner::SetParaFlag( Paragraph* pPara, ParaFlag nFlag )
1963 if( pPara && !pPara->HasFlag( nFlag ) )
1965 if( IsUndoEnabled() && !IsInUndo() )
1966 InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( this, GetAbsPos( pPara ), pPara->nFlags, pPara->nFlags|nFlag ) );
1968 pPara->SetFlag( nFlag );
1972 bool Outliner::HasParaFlag( const Paragraph* pPara, ParaFlag nFlag )
1974 return pPara && pPara->HasFlag( nFlag );
1978 bool Outliner::IsPageOverflow()
1980 return pEditEngine->IsPageOverflow();
1983 std::optional<NonOverflowingText> Outliner::GetNonOverflowingText() const
1985 /* XXX:
1986 * nCount should be the number of paragraphs of the non overflowing text
1987 * nStart should be the starting paragraph of the non overflowing text (XXX: Always 0?)
1990 if ( GetParagraphCount() < 1 )
1991 return {};
1993 // last non-overflowing paragraph is before the first overflowing one
1994 sal_Int32 nCount = pEditEngine->GetOverflowingParaNum();
1995 sal_Int32 nOverflowLine = pEditEngine->GetOverflowingLineNum(); // XXX: Unused for now
1997 // Defensive check: overflowing para index beyond actual # of paragraphs?
1998 if ( nCount > GetParagraphCount()-1) {
1999 SAL_INFO("editeng.chaining",
2000 "[Overflowing] Ops, trying to retrieve para "
2001 << nCount << " when max index is " << GetParagraphCount()-1 );
2002 return {};
2005 if (nCount < 0)
2007 SAL_INFO("editeng.chaining",
2008 "[Overflowing] No Overflowing text but GetNonOverflowinText called?!");
2009 return {};
2012 // NOTE: We want the selection of the overflowing text from here
2013 // At the same time we may want to consider the beginning of such text
2014 // in a more fine grained way (i.e. as GetNonOverflowingText did)
2017 sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
2018 sal_uInt32 nParaCount = GetParagraphCount();
2020 sal_uInt32 nLen = 0;
2021 for ( sal_Int32 nLine = 0;
2022 nLine < pEditEngine->GetOverflowingLineNum();
2023 nLine++) {
2024 nLen += GetLineLen(nHeadPara, nLine);
2027 sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
2028 ESelection aOverflowingTextSel;
2029 sal_Int32 nLastPara = nParaCount-1;
2030 sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
2031 aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
2032 nLastPara, nLastParaLen);
2033 bool bLastParaInterrupted =
2034 pEditEngine->GetOverflowingLineNum() > 0;
2036 return new NonOverflowingText(aOverflowingTextSel, bLastParaInterrupted);
2040 // Only overflowing text, i.e. 1st line of 1st paragraph overflowing
2041 bool bItAllOverflew = nCount == 0 && nOverflowLine == 0;
2042 if ( bItAllOverflew )
2044 ESelection aEmptySel(0,0,0,0);
2045 //EditTextObject *pTObj = pEditEngine->CreateTextObject(aEmptySel);
2046 bool const bLastParaInterrupted = true; // Last Para was interrupted since everything overflew
2047 return NonOverflowingText(aEmptySel, bLastParaInterrupted);
2048 } else { // Get the lines that of the overflowing para fit in the box
2050 sal_Int32 nOverflowingPara = nCount;
2051 sal_uInt32 nLen = 0;
2053 for ( sal_Int32 nLine = 0;
2054 nLine < pEditEngine->GetOverflowingLineNum();
2055 nLine++)
2057 nLen += GetLineLen(nOverflowingPara, nLine);
2060 //sal_Int32 nStartPara = 0;
2061 //sal_Int32 nStartPos = 0;
2062 ESelection aOverflowingTextSelection;
2064 const sal_Int32 nEndPara = GetParagraphCount()-1;
2065 const sal_Int32 nEndPos = pEditEngine->GetTextLen(nEndPara);
2067 if (nLen == 0) {
2068 // XXX: What happens inside this case might be dependent on the joining paragraph or not-thingy
2069 // Overflowing paragraph is empty or first line overflowing: it's not "Non-Overflowing" text then
2070 sal_Int32 nParaLen = GetText(GetParagraph(nOverflowingPara-1)).getLength();
2071 aOverflowingTextSelection =
2072 ESelection(nOverflowingPara-1, nParaLen, nEndPara, nEndPos);
2073 } else {
2074 // We take until we have to from the overflowing paragraph
2075 aOverflowingTextSelection =
2076 ESelection(nOverflowingPara, nLen, nEndPara, nEndPos);
2078 //EditTextObject *pTObj = pEditEngine->CreateTextObject(aNonOverflowingTextSelection);
2080 //sal_Int32 nLastLine = GetLineCount(nOverflowingPara)-1;
2081 bool bLastParaInterrupted =
2082 pEditEngine->GetOverflowingLineNum() > 0;
2084 return NonOverflowingText(aOverflowingTextSelection, bLastParaInterrupted);
2088 OutlinerParaObject Outliner::GetEmptyParaObject() const
2090 std::unique_ptr<EditTextObject> pEmptyText = pEditEngine->GetEmptyTextObject();
2091 OutlinerParaObject aPObj( std::move(pEmptyText) );
2092 aPObj.SetOutlinerMode(GetOutlinerMode());
2093 return aPObj;
2096 std::optional<OverflowingText> Outliner::GetOverflowingText() const
2098 if ( pEditEngine->GetOverflowingParaNum() < 0)
2099 return {};
2102 // Defensive check: overflowing para index beyond actual # of paragraphs?
2103 if ( pEditEngine->GetOverflowingParaNum() > GetParagraphCount()-1) {
2104 SAL_INFO("editeng.chaining",
2105 "[Overflowing] Ops, trying to retrieve para "
2106 << pEditEngine->GetOverflowingParaNum() << " when max index is "
2107 << GetParagraphCount()-1 );
2108 return {};
2111 sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
2112 sal_uInt32 nParaCount = GetParagraphCount();
2114 sal_uInt32 nLen = 0;
2115 for ( sal_Int32 nLine = 0;
2116 nLine < pEditEngine->GetOverflowingLineNum();
2117 nLine++) {
2118 nLen += GetLineLen(nHeadPara, nLine);
2121 sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
2122 ESelection aOverflowingTextSel;
2123 sal_Int32 nLastPara = nParaCount-1;
2124 sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
2125 aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
2126 nLastPara, nLastParaLen);
2127 return OverflowingText(pEditEngine->CreateTransferable(aOverflowingTextSel));
2131 void Outliner::ClearOverflowingParaNum()
2133 pEditEngine->ClearOverflowingParaNum();
2136 void Outliner::dumpAsXml(xmlTextWriterPtr pWriter) const
2138 bool bOwns = false;
2139 if (!pWriter)
2141 pWriter = xmlNewTextWriterFilename("outliner.xml", 0);
2142 xmlTextWriterSetIndent(pWriter,1);
2143 (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
2144 (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
2145 bOwns = true;
2148 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Outliner"));
2149 pParaList->dumpAsXml(pWriter);
2150 (void)xmlTextWriterEndElement(pWriter);
2152 if (bOwns)
2154 (void)xmlTextWriterEndDocument(pWriter);
2155 xmlFreeTextWriter(pWriter);
2159 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */