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 <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>
27
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>
53
54 #include <memory>
55 using std::advance;
56
57
58 // Outliner
59
60
ImplCheckDepth(sal_Int16 & rnDepth) const61 void Outliner::ImplCheckDepth( sal_Int16& rnDepth ) const
62 {
63 if( rnDepth < gnMinDepth )
64 rnDepth = gnMinDepth;
65 else if( rnDepth > nMaxDepth )
66 rnDepth = nMaxDepth;
67 }
68
Insert(const OUString & rText,sal_Int32 nAbsPos,sal_Int16 nDepth)69 Paragraph* Outliner::Insert(const OUString& rText, sal_Int32 nAbsPos, sal_Int16 nDepth)
70 {
71 DBG_ASSERT(pParaList->GetParagraphCount(),"Insert:No Paras");
72
73 Paragraph* pPara;
74
75 ImplCheckDepth( nDepth );
76
77 sal_Int32 nParagraphCount = pParaList->GetParagraphCount();
78 if( nAbsPos > nParagraphCount )
79 nAbsPos = nParagraphCount;
80
81 if( bFirstParaIsEmpty )
82 {
83 pPara = pParaList->GetParagraph( 0 );
84 if( pPara->GetDepth() != nDepth )
85 {
86 nDepthChangedHdlPrevDepth = pPara->GetDepth();
87 ParaFlag nPrevFlags = pPara->nFlags;
88 pPara->SetDepth( nDepth );
89 DepthChangedHdl(pPara, nPrevFlags);
90 }
91 pPara->nFlags |= ParaFlag::HOLDDEPTH;
92 SetText( rText, pPara );
93 }
94 else
95 {
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 );
108 }
109 bFirstParaIsEmpty = false;
110 DBG_ASSERT(pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(),"SetText failed");
111 return pPara;
112 }
113
114
ParagraphInserted(sal_Int32 nPara)115 void Outliner::ParagraphInserted( sal_Int32 nPara )
116 {
117
118 if ( nBlockInsCallback )
119 return;
120
121 if( bPasting || pEditEngine->IsInUndo() )
122 {
123 Paragraph* pPara = new Paragraph( -1 );
124 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
125 if( pEditEngine->IsInUndo() )
126 {
127 pPara->bVisible = true;
128 const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
129 pPara->SetDepth( rLevel.GetValue() );
130 }
131 }
132 else
133 {
134 sal_Int16 nDepth = -1;
135 Paragraph* pParaBefore = pParaList->GetParagraph( nPara-1 );
136 if ( pParaBefore )
137 nDepth = pParaBefore->GetDepth();
138
139 Paragraph* pPara = new Paragraph( nDepth );
140 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
141
142 if( !pEditEngine->IsInUndo() )
143 {
144 ImplCalcBulletText( nPara, true, false );
145 ParagraphInsertedHdl(pPara);
146 }
147 }
148 }
149
ParagraphDeleted(sal_Int32 nPara)150 void Outliner::ParagraphDeleted( sal_Int32 nPara )
151 {
152
153 if ( nBlockInsCallback || ( nPara == EE_PARA_ALL ) )
154 return;
155
156 Paragraph* pPara = pParaList->GetParagraph( nPara );
157 if (!pPara)
158 return;
159
160 sal_Int16 nDepth = pPara->GetDepth();
161
162 if( !pEditEngine->IsInUndo() )
163 {
164 aParaRemovingHdl.Call( { this, pPara } );
165 }
166
167 pParaList->Remove( nPara );
168
169 if( pEditEngine->IsInUndo() || bPasting )
170 return;
171
172 pPara = pParaList->GetParagraph( nPara );
173 if ( pPara && ( pPara->GetDepth() > nDepth ) )
174 {
175 ImplCalcBulletText( nPara, true, false );
176 // Search for next on the this level ...
177 while ( pPara && pPara->GetDepth() > nDepth )
178 pPara = pParaList->GetParagraph( ++nPara );
179 }
180
181 if ( pPara && ( pPara->GetDepth() == nDepth ) )
182 ImplCalcBulletText( nPara, true, false );
183 }
184
Init(OutlinerMode nMode)185 void Outliner::Init( OutlinerMode nMode )
186 {
187 nOutlinerMode = nMode;
188
189 Clear();
190
191 EEControlBits nCtrl = pEditEngine->GetControlWord();
192 nCtrl &= ~EEControlBits(EEControlBits::OUTLINER|EEControlBits::OUTLINER2);
193
194 SetMaxDepth( 9 );
195
196 switch ( GetOutlinerMode() )
197 {
198 case OutlinerMode::TextObject:
199 case OutlinerMode::TitleObject:
200 break;
201
202 case OutlinerMode::OutlineObject:
203 nCtrl |= EEControlBits::OUTLINER2;
204 break;
205 case OutlinerMode::OutlineView:
206 nCtrl |= EEControlBits::OUTLINER;
207 break;
208
209 default: OSL_FAIL( "Outliner::Init - Invalid Mode!" );
210 }
211
212 pEditEngine->SetControlWord( nCtrl );
213
214 const bool bWasUndoEnabled(IsUndoEnabled());
215 EnableUndo(false);
216 ImplInitDepth( 0, -1, false );
217 GetUndoManager().Clear();
218 EnableUndo(bWasUndoEnabled);
219 }
220
SetMaxDepth(sal_Int16 nDepth)221 void Outliner::SetMaxDepth( sal_Int16 nDepth )
222 {
223 if( nMaxDepth != nDepth )
224 {
225 nMaxDepth = std::min( nDepth, sal_Int16(SVX_MAX_NUM-1) );
226 }
227 }
228
GetDepth(sal_Int32 nPara) const229 sal_Int16 Outliner::GetDepth( sal_Int32 nPara ) const
230 {
231 Paragraph* pPara = pParaList->GetParagraph( nPara );
232 DBG_ASSERT( pPara, "Outliner::GetDepth - Paragraph not found!" );
233 return pPara ? pPara->GetDepth() : -1;
234 }
235
SetDepth(Paragraph * pPara,sal_Int16 nNewDepth)236 void Outliner::SetDepth( Paragraph* pPara, sal_Int16 nNewDepth )
237 {
238
239 ImplCheckDepth( nNewDepth );
240
241 if ( nNewDepth == pPara->GetDepth() )
242 return;
243
244 nDepthChangedHdlPrevDepth = pPara->GetDepth();
245 ParaFlag nPrevFlags = pPara->nFlags;
246
247 sal_Int32 nPara = GetAbsPos( pPara );
248 ImplInitDepth( nPara, nNewDepth, true );
249 ImplCalcBulletText( nPara, false, false );
250
251 if ( GetOutlinerMode() == OutlinerMode::OutlineObject )
252 ImplSetLevelDependentStyleSheet( nPara );
253
254 DepthChangedHdl(pPara, nPrevFlags);
255 }
256
GetNumberingStartValue(sal_Int32 nPara) const257 sal_Int16 Outliner::GetNumberingStartValue( sal_Int32 nPara ) const
258 {
259 Paragraph* pPara = pParaList->GetParagraph( nPara );
260 DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
261 return pPara ? pPara->GetNumberingStartValue() : -1;
262 }
263
SetNumberingStartValue(sal_Int32 nPara,sal_Int16 nNumberingStartValue)264 void Outliner::SetNumberingStartValue( sal_Int32 nPara, sal_Int16 nNumberingStartValue )
265 {
266 Paragraph* pPara = pParaList->GetParagraph( nPara );
267 DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
268 if( pPara && pPara->GetNumberingStartValue() != nNumberingStartValue )
269 {
270 if( IsUndoEnabled() && !IsInUndo() )
271 InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
272 pPara->GetNumberingStartValue(), nNumberingStartValue,
273 pPara->IsParaIsNumberingRestart(), pPara->IsParaIsNumberingRestart() ) );
274
275 pPara->SetNumberingStartValue( nNumberingStartValue );
276 ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
277 pEditEngine->SetModified();
278 }
279 }
280
IsParaIsNumberingRestart(sal_Int32 nPara) const281 bool Outliner::IsParaIsNumberingRestart( sal_Int32 nPara ) const
282 {
283 Paragraph* pPara = pParaList->GetParagraph( nPara );
284 DBG_ASSERT( pPara, "Outliner::IsParaIsNumberingRestart - Paragraph not found!" );
285 return pPara && pPara->IsParaIsNumberingRestart();
286 }
287
SetParaIsNumberingRestart(sal_Int32 nPara,bool bParaIsNumberingRestart)288 void Outliner::SetParaIsNumberingRestart( sal_Int32 nPara, bool bParaIsNumberingRestart )
289 {
290 Paragraph* pPara = pParaList->GetParagraph( nPara );
291 DBG_ASSERT( pPara, "Outliner::SetParaIsNumberingRestart - Paragraph not found!" );
292 if( pPara && (pPara->IsParaIsNumberingRestart() != bParaIsNumberingRestart) )
293 {
294 if( IsUndoEnabled() && !IsInUndo() )
295 InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
296 pPara->GetNumberingStartValue(), pPara->GetNumberingStartValue(),
297 pPara->IsParaIsNumberingRestart(), bParaIsNumberingRestart ) );
298
299 pPara->SetParaIsNumberingRestart( bParaIsNumberingRestart );
300 ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
301 pEditEngine->SetModified();
302 }
303 }
304
GetBulletsNumberingStatus(const sal_Int32 nParaStart,const sal_Int32 nParaEnd) const305 sal_Int32 Outliner::GetBulletsNumberingStatus(
306 const sal_Int32 nParaStart,
307 const sal_Int32 nParaEnd ) const
308 {
309 if ( nParaStart > nParaEnd
310 || nParaEnd >= pParaList->GetParagraphCount() )
311 {
312 SAL_WARN("editeng", "<Outliner::GetBulletsNumberingStatus> - unexpected parameter values" );
313 return 2;
314 }
315
316 sal_Int32 nBulletsCount = 0;
317 sal_Int32 nNumberingCount = 0;
318 for (sal_Int32 nPara = nParaStart; nPara <= nParaEnd; ++nPara)
319 {
320 if ( !pParaList->GetParagraph(nPara) )
321 {
322 break;
323 }
324 const SvxNumberFormat* pFmt = GetNumberFormat(nPara);
325 if (!pFmt)
326 {
327 // At least, exists one paragraph that has no Bullets/Numbering.
328 break;
329 }
330 else if ((pFmt->GetNumberingType() == SVX_NUM_BITMAP) || (pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL))
331 {
332 // Having Bullets in this paragraph.
333 nBulletsCount++;
334 }
335 else
336 {
337 // Having Numbering in this paragraph.
338 nNumberingCount++;
339 }
340 }
341
342 const sal_Int32 nParaCount = nParaEnd - nParaStart + 1;
343 if ( nBulletsCount == nParaCount )
344 {
345 return 0;
346 }
347 else if ( nNumberingCount == nParaCount )
348 {
349 return 1;
350 }
351 return 2;
352 }
353
GetBulletsNumberingStatus() const354 sal_Int32 Outliner::GetBulletsNumberingStatus() const
355 {
356 return pParaList->GetParagraphCount() > 0
357 ? GetBulletsNumberingStatus( 0, pParaList->GetParagraphCount()-1 )
358 : 2;
359 }
360
CreateParaObject(sal_Int32 nStartPara,sal_Int32 nCount) const361 std::optional<OutlinerParaObject> Outliner::CreateParaObject( sal_Int32 nStartPara, sal_Int32 nCount ) const
362 {
363 if ( static_cast<sal_uInt64>(nStartPara) + nCount >
364 o3tl::make_unsigned(pParaList->GetParagraphCount()) )
365 nCount = pParaList->GetParagraphCount() - nStartPara;
366
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;
371
372 if (nCount <= 0)
373 return std::nullopt;
374
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);
379
380 for(sal_Int32 nPara(nStartPara); nPara <= nLastPara; nPara++)
381 {
382 aParagraphDataVector[nPara-nStartPara] = *GetParagraph(nPara);
383 }
384
385 xText->ClearPortionInfo(); // tdf#147166 the PortionInfo is unwanted here
386 OutlinerParaObject aPObj(std::move(xText), std::move(aParagraphDataVector), bIsEditDoc);
387 aPObj.SetOutlinerMode(GetOutlinerMode());
388
389 return aPObj;
390 }
391
SetToEmptyText()392 void Outliner::SetToEmptyText()
393 {
394 SetText(GetEmptyParaObject());
395 }
396
SetText(const OUString & rText,Paragraph * pPara)397 void Outliner::SetText( const OUString& rText, Paragraph* pPara )
398 {
399 assert(pPara && "SetText:No Para");
400
401 const sal_Int32 nPara = pParaList->GetAbsPos( pPara );
402
403 if (pEditEngine->GetText( nPara ) == rText)
404 {
405 // short-circuit logic to improve performance
406 bFirstParaIsEmpty = false;
407 return;
408 }
409
410 const bool bUpdate = pEditEngine->SetUpdateLayout( false );
411 ImplBlockInsertionCallbacks( true );
412
413 if (rText.isEmpty())
414 {
415 pEditEngine->SetText( nPara, rText );
416 ImplInitDepth( nPara, pPara->GetDepth(), false );
417 }
418 else
419 {
420 const OUString aText(convertLineEnd(rText, LINEEND_LF));
421
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() )
430 {
431 std::u16string_view aStr = o3tl::getToken(aText, 0, '\x0A', nIdx );
432
433 sal_Int16 nCurDepth;
434 if( nPos )
435 {
436 pPara = new Paragraph( -1 );
437 nCurDepth = -1;
438 }
439 else
440 nCurDepth = pPara->GetDepth();
441
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 ) )
446 {
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);
453
454 // Keep depth? (see Outliner::Insert)
455 if( !(pPara->nFlags & ParaFlag::HOLDDEPTH) )
456 {
457 nCurDepth = nTabs-1; //TODO: sal_Int32 -> sal_Int16!
458 ImplCheckDepth( nCurDepth );
459 pPara->SetDepth( nCurDepth );
460 }
461 }
462 if( nPos ) // not with the first paragraph
463 {
464 pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nInsPos );
465 pEditEngine->InsertParagraph( nInsPos, OUString(aStr) );
466 ParagraphInsertedHdl(pPara);
467 }
468 else
469 {
470 nInsPos--;
471 pEditEngine->SetText( nInsPos, OUString(aStr) );
472 }
473 ImplInitDepth( nInsPos, nCurDepth, false );
474 nInsPos++;
475 nPos++;
476 }
477 }
478
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);
484 }
485
486 // pView == 0 -> Ignore tabs
487
ImpConvertEdtToOut(sal_Int32 nPara)488 bool Outliner::ImpConvertEdtToOut( sal_Int32 nPara )
489 {
490
491 bool bConverted = false;
492 sal_Int32 nTabs = 0;
493 ESelection aDelSel;
494
495 OUString aName;
496
497 OUString aStr( pEditEngine->GetText( nPara ) );
498 const sal_Unicode* pPtr = aStr.getStr();
499
500 sal_Int32 nHeadingNumberStart = 0;
501 sal_Int32 nNumberingNumberStart = 0;
502 SfxStyleSheet* pStyle= pEditEngine->GetStyleSheet( nPara );
503 if( pStyle )
504 {
505 OUString aHeading_US( u"heading"_ustr );
506 OUString aNumber_US( u"Numbering"_ustr );
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();
513 }
514
515 if ( nHeadingNumberStart || nNumberingNumberStart )
516 {
517 // PowerPoint import ?
518 if( nHeadingNumberStart && ( aStr.getLength() >= 2 ) &&
519 ( pPtr[0] != '\t' ) && ( pPtr[1] == '\t' ) )
520 {
521 // Extract Bullet and Tab
522 aDelSel = ESelection( nPara, 0, nPara, 2 );
523 }
524
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;
531 }
532 else
533 {
534 // filter leading tabs
535 while( *pPtr == '\t' )
536 {
537 pPtr++;
538 nTabs++;
539 }
540 // Remove tabs from the text
541 if( nTabs )
542 aDelSel = ESelection( nPara, 0, nPara, nTabs );
543 }
544
545 if ( aDelSel.HasRange() )
546 {
547 pEditEngine->QuickDelete( aDelSel );
548 }
549
550 const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
551 sal_Int16 nOutlLevel = rLevel.GetValue();
552
553 ImplCheckDepth( nOutlLevel );
554 ImplInitDepth( nPara, nOutlLevel, false );
555
556 return bConverted;
557 }
558
SetText(const OutlinerParaObject & rPObj)559 void Outliner::SetText( const OutlinerParaObject& rPObj )
560 {
561 bool bUpdate = pEditEngine->SetUpdateLayout( false );
562
563 bool bUndo = pEditEngine->IsUndoEnabled();
564 EnableUndo( false );
565
566 Init( rPObj.GetOutlinerMode() );
567
568 ImplBlockInsertionCallbacks( true );
569 pEditEngine->SetText(rPObj.GetTextObject());
570
571 bFirstParaIsEmpty = false;
572
573 pParaList->Clear();
574 for( sal_Int32 nCurPara = 0; nCurPara < rPObj.Count(); nCurPara++ )
575 {
576 std::unique_ptr<Paragraph> pPara(new Paragraph( rPObj.GetParagraphData(nCurPara)));
577 ImplCheckDepth( pPara->nDepth );
578
579 pParaList->Append(std::move(pPara));
580 ImplCheckNumBulletItem( nCurPara );
581 }
582
583 ImplCheckParagraphs( 0, pParaList->GetParagraphCount() );
584
585 EnableUndo( bUndo );
586 ImplBlockInsertionCallbacks( false );
587 pEditEngine->SetUpdateLayout( bUpdate );
588
589 DBG_ASSERT( pParaList->GetParagraphCount()==rPObj.Count(),"SetText failed");
590 DBG_ASSERT( pEditEngine->GetParagraphCount()==rPObj.Count(),"SetText failed");
591 }
592
AddText(const OutlinerParaObject & rPObj,bool bAppend)593 void Outliner::AddText( const OutlinerParaObject& rPObj, bool bAppend )
594 {
595 bool bUpdate = pEditEngine->SetUpdateLayout( false );
596
597 ImplBlockInsertionCallbacks( true );
598 sal_Int32 nPara;
599 if( bFirstParaIsEmpty )
600 {
601 pParaList->Clear();
602 pEditEngine->SetText(rPObj.GetTextObject());
603 nPara = 0;
604 bAppend = false;
605 }
606 else
607 {
608 nPara = pParaList->GetParagraphCount();
609 pEditEngine->InsertParagraph( EE_PARA_APPEND, rPObj.GetTextObject(), bAppend );
610 }
611 bFirstParaIsEmpty = false;
612
613 for( sal_Int32 n = 0; n < rPObj.Count(); n++ )
614 {
615 if ( n == 0 && bAppend )
616 {
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;
621 }
622
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 );
628 }
629 DBG_ASSERT( pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(), "SetText: OutOfSync" );
630
631 ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
632
633 ImplBlockInsertionCallbacks( false );
634 pEditEngine->SetUpdateLayout( bUpdate );
635 }
636
CalcFieldValue(const SvxFieldItem & rField,sal_Int32 nPara,sal_Int32 nPos,std::optional<Color> & rpTxtColor,std::optional<Color> & rpFldColor,std::optional<FontLineStyle> & rpFldLineStyle)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 )
638 {
639 if ( !aCalcFieldValueHdl.IsSet() )
640 return OUString( ' ' );
641
642 EditFieldInfo aFldInfo( this, rField, nPara, nPos );
643 // The FldColor is preset with COL_LIGHTGRAY.
644 if ( rpFldColor )
645 aFldInfo.SetFieldColor( *rpFldColor );
646
647 aCalcFieldValueHdl.Call( &aFldInfo );
648 if ( aFldInfo.GetTextColor() )
649 {
650 rpTxtColor = *aFldInfo.GetTextColor();
651 }
652
653 if ( aFldInfo.GetFontLineStyle() )
654 {
655 rpFldLineStyle = *aFldInfo.GetFontLineStyle();
656 }
657
658 if (aFldInfo.GetFieldColor())
659 rpFldColor = *aFldInfo.GetFieldColor();
660 else
661 rpFldColor.reset();
662
663 return aFldInfo.GetRepresentation();
664 }
665
SetStyleSheet(sal_Int32 nPara,SfxStyleSheet * pStyle)666 void Outliner::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
667 {
668 Paragraph* pPara = pParaList->GetParagraph( nPara );
669 if (pPara)
670 {
671 pEditEngine->SetStyleSheet( nPara, pStyle );
672 ImplCheckNumBulletItem( nPara );
673 }
674 }
675
ImplCheckNumBulletItem(sal_Int32 nPara)676 void Outliner::ImplCheckNumBulletItem( sal_Int32 nPara )
677 {
678 Paragraph* pPara = pParaList->GetParagraph( nPara );
679 if (pPara)
680 pPara->aBulSize.setWidth( -1 );
681 }
682
ImplSetLevelDependentStyleSheet(sal_Int32 nPara)683 void Outliner::ImplSetLevelDependentStyleSheet( sal_Int32 nPara )
684 {
685
686 DBG_ASSERT( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) || ( GetOutlinerMode() == OutlinerMode::OutlineView ), "SetLevelDependentStyleSheet: Wrong Mode!" );
687
688 SfxStyleSheet* pStyle = GetStyleSheet( nPara );
689
690 if ( !pStyle )
691 return;
692
693 sal_Int16 nDepth = GetDepth( nPara );
694 if( nDepth < 0 )
695 nDepth = 0;
696
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 ) ) )
703 {
704 SfxItemSet aOldAttrs( GetParaAttribs( nPara ) );
705 SetStyleSheet( nPara, pNewStyle );
706 if ( aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET )
707 {
708 SfxItemSet aAttrs( GetParaAttribs( nPara ) );
709 aAttrs.Put( aOldAttrs.Get( EE_PARA_NUMBULLET ) );
710 SetParaAttribs( nPara, aAttrs );
711 }
712 }
713 }
714
ImplInitDepth(sal_Int32 nPara,sal_Int16 nDepth,bool bCreateUndo)715 void Outliner::ImplInitDepth( sal_Int32 nPara, sal_Int16 nDepth, bool bCreateUndo )
716 {
717
718 DBG_ASSERT( ( nDepth >= gnMinDepth ) && ( nDepth <= nMaxDepth ), "ImplInitDepth - Depth is invalid!" );
719
720 Paragraph* pPara = pParaList->GetParagraph( nPara );
721 if (!pPara)
722 return;
723 sal_Int16 nOldDepth = pPara->GetDepth();
724 pPara->SetDepth( nDepth );
725
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;
730
731 bool bUpdate = pEditEngine->SetUpdateLayout( false );
732
733 bool bUndo = bCreateUndo && IsUndoEnabled();
734
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 );
740
741 if ( bUndo )
742 {
743 InsertUndo( std::make_unique<OutlinerUndoChangeDepth>( this, nPara, nOldDepth, nDepth ) );
744 }
745
746 pEditEngine->SetUpdateLayout( bUpdate );
747 }
748
SetParaAttribs(sal_Int32 nPara,const SfxItemSet & rSet)749 void Outliner::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
750 {
751
752 pEditEngine->SetParaAttribs( nPara, rSet );
753 }
754
SetCharAttribs(sal_Int32 nPara,const SfxItemSet & rSet)755 void Outliner::SetCharAttribs(sal_Int32 nPara, const SfxItemSet& rSet)
756 {
757 pEditEngine->SetCharAttribs(nPara, rSet);
758 }
759
Expand(Paragraph const * pPara)760 bool Outliner::Expand( Paragraph const * pPara )
761 {
762 if ( !pParaList->HasHiddenChildren( pPara ) )
763 return false;
764
765 std::unique_ptr<OLUndoExpand> pUndo;
766 bool bUndo = IsUndoEnabled() && !IsInUndo();
767 if( bUndo )
768 {
769 UndoActionStart( OLUNDO_EXPAND );
770 pUndo.reset( new OLUndoExpand( this, OLUNDO_EXPAND ) );
771 pUndo->nCount = pParaList->GetAbsPos( pPara );
772 }
773 pParaList->Expand( pPara );
774 InvalidateBullet(pParaList->GetAbsPos(pPara));
775 if( bUndo )
776 {
777 InsertUndo( std::move(pUndo) );
778 UndoActionEnd();
779 }
780 return true;
781 }
782
Collapse(Paragraph const * pPara)783 bool Outliner::Collapse( Paragraph const * pPara )
784 {
785 if ( !pParaList->HasVisibleChildren( pPara ) ) // collapsed
786 return false;
787
788 std::unique_ptr<OLUndoExpand> pUndo;
789 bool bUndo = false;
790
791 if( !IsInUndo() && IsUndoEnabled() )
792 bUndo = true;
793 if( bUndo )
794 {
795 UndoActionStart( OLUNDO_COLLAPSE );
796 pUndo.reset( new OLUndoExpand( this, OLUNDO_COLLAPSE ) );
797 pUndo->nCount = pParaList->GetAbsPos( pPara );
798 }
799
800 pParaList->Collapse( pPara );
801 InvalidateBullet(pParaList->GetAbsPos(pPara));
802 if( bUndo )
803 {
804 InsertUndo( std::move(pUndo) );
805 UndoActionEnd();
806 }
807 return true;
808 }
809
ImpCalcBulletFont(sal_Int32 nPara) const810 vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) const
811 {
812 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
813 assert(pFmt && "ImpCalcBulletFont: Missing!");
814 DBG_ASSERT(( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ), "ImpCalcBulletFont: BitmapBullet!" );
815
816 vcl::Font aStdFont;
817 if ( !pEditEngine->IsFlatMode() )
818 {
819 ESelection aSel( nPara, 0, nPara, 0 );
820 aStdFont = EditEngine::CreateFontFromItemSet( pEditEngine->GetAttribs( aSel ), pEditEngine->GetScriptType( aSel ) );
821 }
822 else
823 {
824 aStdFont = pEditEngine->GetStandardFont( nPara );
825 }
826
827 vcl::Font aBulletFont;
828 std::optional<vcl::Font> pSourceFont;
829 if ( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
830 {
831 pSourceFont = pFmt->GetBulletFont();
832 }
833
834 if (pSourceFont)
835 {
836 aBulletFont = *pSourceFont;
837 }
838 else
839 {
840 aBulletFont = aStdFont;
841 aBulletFont.SetUnderline( LINESTYLE_NONE );
842 aBulletFont.SetOverline( LINESTYLE_NONE );
843 aBulletFont.SetStrikeout( STRIKEOUT_NONE );
844 aBulletFont.SetEmphasisMark( FontEmphasisMark::NONE );
845 aBulletFont.SetRelief( FontRelief::NONE );
846 }
847
848 // Use original scale...
849
850 double fFontScaleY = pFmt->GetBulletRelSize() / 100.0 * getScalingParameters().fFontY;
851 double fScaledLineHeight = aStdFont.GetFontSize().Height() * fFontScaleY;
852
853 aBulletFont.SetAlignment( ALIGN_BOTTOM );
854 aBulletFont.SetFontSize(Size(0, basegfx::fround(fScaledLineHeight)));
855 bool bVertical = IsVertical();
856 aBulletFont.SetVertical( bVertical );
857 aBulletFont.SetOrientation( Degree10(bVertical ? (IsTopToBottom() ? 2700 : 900) : 0) );
858
859 Color aColor( COL_AUTO );
860 if( !pEditEngine->IsFlatMode() && !( pEditEngine->GetControlWord() & EEControlBits::NOCOLORS ) )
861 {
862 aColor = pFmt->GetBulletColor();
863 }
864
865 if ( ( aColor == COL_AUTO ) || ( IsForceAutoColor() ) )
866 aColor = pEditEngine->GetAutoColor();
867
868 aBulletFont.SetColor( aColor );
869 return aBulletFont;
870 }
871
PaintBullet(sal_Int32 nPara,const Point & rStartPos,const Point & rOrigin,Degree10 nOrientation,OutputDevice & rOutDev)872 void Outliner::PaintBullet(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin,
873 Degree10 nOrientation, OutputDevice& rOutDev)
874 {
875
876 bool bDrawBullet = false;
877 if (pEditEngine)
878 {
879 const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
880 bDrawBullet = rBulletState.GetValue();
881 }
882
883 if (!(bDrawBullet && ImplHasNumberFormat(nPara)))
884 return;
885
886 bool bVertical = IsVertical();
887 bool bTopToBottom = IsTopToBottom();
888
889 bool bRightToLeftPara = pEditEngine->IsRightToLeft( nPara );
890
891 tools::Rectangle aBulletArea( ImpCalcBulletArea( nPara, true, false ) );
892
893 double fSpacingFactorX = getScalingParameters().fSpacingX;
894
895 tools::Long nStretchBulletX = basegfx::fround<tools::Long>(double(aBulletArea.Left()) * fSpacingFactorX);
896 tools::Long nStretchBulletWidth = basegfx::fround<tools::Long>(double(aBulletArea.GetWidth()) * fSpacingFactorX);
897 aBulletArea = tools::Rectangle(Point(nStretchBulletX, aBulletArea.Top()),
898 Size(nStretchBulletWidth, aBulletArea.GetHeight()) );
899
900 Paragraph* pPara = pParaList->GetParagraph( nPara );
901 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
902 if ( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) )
903 {
904 if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
905 {
906 vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
907 // Use baseline
908 bool bSymbol = pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL;
909 aBulletFont.SetAlignment( bSymbol ? ALIGN_BOTTOM : ALIGN_BASELINE );
910 vcl::Font aOldFont = rOutDev.GetFont();
911 rOutDev.SetFont( aBulletFont );
912
913 ParagraphInfos aParaInfos = pEditEngine->GetParagraphInfos( nPara );
914 Point aTextPos;
915 if ( !bVertical )
916 {
917 // aTextPos.Y() = rStartPos.Y() + aBulletArea.Bottom();
918 aTextPos.setY( rStartPos.Y() + ( bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent ) );
919 if ( !bRightToLeftPara )
920 aTextPos.setX( rStartPos.X() + aBulletArea.Left() );
921 else
922 aTextPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
923 }
924 else
925 {
926 if (bTopToBottom)
927 {
928 // aTextPos.X() = rStartPos.X() - aBulletArea.Bottom();
929 aTextPos.setX( rStartPos.X() - (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
930 aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
931 }
932 else
933 {
934 aTextPos.setX( rStartPos.X() + (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
935 aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
936 }
937 }
938
939 if ( nOrientation )
940 {
941 // Both TopLeft and bottom left is not quite correct,
942 // since in EditEngine baseline ...
943 rOrigin.RotateAround(aTextPos, nOrientation);
944
945 vcl::Font aRotatedFont( aBulletFont );
946 aRotatedFont.SetOrientation( nOrientation );
947 rOutDev.SetFont( aRotatedFont );
948 }
949
950 // VCL will take care of brackets and so on...
951 vcl::text::ComplexTextLayoutFlags nLayoutMode = rOutDev.GetLayoutMode();
952 nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags(vcl::text::ComplexTextLayoutFlags::BiDiRtl|vcl::text::ComplexTextLayoutFlags::BiDiStrong);
953 if ( bRightToLeftPara )
954 nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
955 rOutDev.SetLayoutMode( nLayoutMode );
956
957 if(bStrippingPortions)
958 {
959 const vcl::Font& aSvxFont(rOutDev.GetFont());
960 KernArray aBuf;
961 rOutDev.GetTextArray( pPara->GetText(), &aBuf );
962
963 if(bSymbol)
964 {
965 // aTextPos is Bottom, go to Baseline
966 FontMetric aMetric(rOutDev.GetFontMetric());
967 aTextPos.AdjustY( -(aMetric.GetDescent()) );
968 }
969
970 assert(aBuf.get_factor() == 1);
971 DrawingText(aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf.get_subunit_array(), {},
972 aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color());
973 }
974 else
975 {
976 rOutDev.DrawText( aTextPos, pPara->GetText() );
977 }
978
979 rOutDev.SetFont( aOldFont );
980 }
981 else
982 {
983 if ( pFmt->GetBrush()->GetGraphicObject() )
984 {
985 Point aBulletPos;
986 if ( !bVertical )
987 {
988 aBulletPos.setY( rStartPos.Y() + aBulletArea.Top() );
989 if ( !bRightToLeftPara )
990 aBulletPos.setX( rStartPos.X() + aBulletArea.Left() );
991 else
992 aBulletPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
993 }
994 else
995 {
996 if (bTopToBottom)
997 {
998 aBulletPos.setX( rStartPos.X() - aBulletArea.Bottom() );
999 aBulletPos.setY( rStartPos.Y() + aBulletArea.Left() );
1000 }
1001 else
1002 {
1003 aBulletPos.setX( rStartPos.X() + aBulletArea.Top() );
1004 aBulletPos.setY( rStartPos.Y() - aBulletArea.Right() );
1005 }
1006 }
1007
1008 if(bStrippingPortions)
1009 {
1010 if(aDrawBulletHdl.IsSet())
1011 {
1012 // call something analog to aDrawPortionHdl (if set) and feed it something
1013 // analog to DrawPortionInfo...
1014 // created aDrawBulletHdl, Set/GetDrawBulletHdl.
1015 // created DrawBulletInfo and added handling to sdrtextdecomposition.cxx
1016 DrawBulletInfo aDrawBulletInfo(
1017 *pFmt->GetBrush()->GetGraphicObject(),
1018 aBulletPos,
1019 pPara->aBulSize);
1020
1021 aDrawBulletHdl.Call(&aDrawBulletInfo);
1022 }
1023 }
1024 else
1025 {
1026 pFmt->GetBrush()->GetGraphicObject()->Draw(rOutDev, aBulletPos, pPara->aBulSize);
1027 }
1028 }
1029 }
1030 }
1031
1032 // In case of collapsed subparagraphs paint a line before the text.
1033 if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) ||
1034 bStrippingPortions || nOrientation )
1035 return;
1036
1037 tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width();
1038
1039 Point aStartPos, aEndPos;
1040 if ( !bVertical )
1041 {
1042 aStartPos.setY( rStartPos.Y() + aBulletArea.Bottom() );
1043 if ( !bRightToLeftPara )
1044 aStartPos.setX( rStartPos.X() + aBulletArea.Right() );
1045 else
1046 aStartPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Left() );
1047 aEndPos = aStartPos;
1048 aEndPos.AdjustX(nWidth );
1049 }
1050 else
1051 {
1052 aStartPos.setX( rStartPos.X() - aBulletArea.Bottom() );
1053 aStartPos.setY( rStartPos.Y() + aBulletArea.Right() );
1054 aEndPos = aStartPos;
1055 aEndPos.AdjustY(nWidth );
1056 }
1057
1058 const Color& rOldLineColor = rOutDev.GetLineColor();
1059 rOutDev.SetLineColor( COL_BLACK );
1060 rOutDev.DrawLine( aStartPos, aEndPos );
1061 rOutDev.SetLineColor( rOldLineColor );
1062 }
1063
InvalidateBullet(sal_Int32 nPara)1064 void Outliner::InvalidateBullet(sal_Int32 nPara)
1065 {
1066 tools::Long nLineHeight = static_cast<tools::Long>(pEditEngine->GetLineHeight(nPara ));
1067 for (OutlinerView* pView : aViewList)
1068 {
1069 Point aPos( pView->pEditView->GetWindowPosTopLeft(nPara ) );
1070 tools::Rectangle aRect( pView->GetOutputArea() );
1071 aRect.SetRight( aPos.X() );
1072 aRect.SetTop( aPos.Y() );
1073 aRect.SetBottom( aPos.Y() );
1074 aRect.AdjustBottom(nLineHeight );
1075
1076 pView->pEditView->InvalidateWindow(aRect);
1077 }
1078 }
1079
Read(SvStream & rInput,const OUString & rBaseURL,EETextFormat eFormat,SvKeyValueIterator * pHTTPHeaderAttrs)1080 ErrCode Outliner::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
1081 {
1082
1083 bool bOldUndo = pEditEngine->IsUndoEnabled();
1084 EnableUndo( false );
1085
1086 bool bUpdate = pEditEngine->SetUpdateLayout( false );
1087
1088 Clear();
1089
1090 ImplBlockInsertionCallbacks( true );
1091 ErrCode nRet = pEditEngine->Read( rInput, rBaseURL, eFormat, pHTTPHeaderAttrs );
1092
1093 bFirstParaIsEmpty = false;
1094
1095 sal_Int32 nParas = pEditEngine->GetParagraphCount();
1096 pParaList->Clear();
1097 for ( sal_Int32 n = 0; n < nParas; n++ )
1098 {
1099 std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
1100 pParaList->Append(std::move(pPara));
1101 }
1102
1103 ImpFilterIndents( 0, nParas-1 );
1104
1105 ImplBlockInsertionCallbacks( false );
1106 pEditEngine->SetUpdateLayout( bUpdate );
1107 EnableUndo( bOldUndo );
1108
1109 return nRet;
1110 }
1111
1112
ImpFilterIndents(sal_Int32 nFirstPara,sal_Int32 nLastPara)1113 void Outliner::ImpFilterIndents( sal_Int32 nFirstPara, sal_Int32 nLastPara )
1114 {
1115 bool bUpdate = pEditEngine->SetUpdateLayout( false );
1116
1117 Paragraph* pLastConverted = nullptr;
1118 for( sal_Int32 nPara = nFirstPara; nPara <= nLastPara; nPara++ )
1119 {
1120 Paragraph* pPara = pParaList->GetParagraph( nPara );
1121 if (pPara)
1122 {
1123 if( ImpConvertEdtToOut( nPara ) )
1124 {
1125 pLastConverted = pPara;
1126 }
1127 else if ( pLastConverted )
1128 {
1129 // Arrange normal paragraphs below the heading ...
1130 pPara->SetDepth( pLastConverted->GetDepth() );
1131 }
1132
1133 ImplInitDepth( nPara, pPara->GetDepth(), false );
1134 }
1135 }
1136
1137 pEditEngine->SetUpdateLayout( bUpdate );
1138 }
1139
GetUndoManager()1140 EditUndoManager& Outliner::GetUndoManager()
1141 {
1142 return pEditEngine->GetUndoManager();
1143 }
1144
SetUndoManager(EditUndoManager * pNew)1145 EditUndoManager* Outliner::SetUndoManager(EditUndoManager* pNew)
1146 {
1147 return pEditEngine->SetUndoManager(pNew);
1148 }
1149
ImpTextPasted(sal_Int32 nStartPara,sal_Int32 nCount)1150 void Outliner::ImpTextPasted( sal_Int32 nStartPara, sal_Int32 nCount )
1151 {
1152 bool bUpdate = pEditEngine->SetUpdateLayout( false );
1153
1154 const sal_Int32 nStart = nStartPara;
1155
1156 Paragraph* pPara = pParaList->GetParagraph( nStartPara );
1157
1158 while( nCount && pPara )
1159 {
1160 if( GetOutlinerMode() != OutlinerMode::TextObject )
1161 {
1162 nDepthChangedHdlPrevDepth = pPara->GetDepth();
1163 ParaFlag nPrevFlags = pPara->nFlags;
1164
1165 ImpConvertEdtToOut( nStartPara );
1166
1167 if( nStartPara == nStart )
1168 {
1169 // the existing paragraph has changed depth or flags
1170 if( (pPara->GetDepth() != nDepthChangedHdlPrevDepth) || (pPara->nFlags != nPrevFlags) )
1171 DepthChangedHdl(pPara, nPrevFlags);
1172 }
1173 }
1174 else // EditEngine mode
1175 {
1176 sal_Int16 nDepth = -1;
1177 const SfxItemSet& rAttrs = pEditEngine->GetParaAttribs( nStartPara );
1178 if ( rAttrs.GetItemState( EE_PARA_OUTLLEVEL ) == SfxItemState::SET )
1179 {
1180 const SfxInt16Item& rLevel = rAttrs.Get( EE_PARA_OUTLLEVEL );
1181 nDepth = rLevel.GetValue();
1182 }
1183 if ( nDepth != GetDepth( nStartPara ) )
1184 ImplInitDepth( nStartPara, nDepth, false );
1185 }
1186
1187 nCount--;
1188 nStartPara++;
1189 pPara = pParaList->GetParagraph( nStartPara );
1190 }
1191
1192 pEditEngine->SetUpdateLayout( bUpdate );
1193
1194 DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"ImpTextPasted failed");
1195 }
1196
IndentingPagesHdl(OutlinerView * pView)1197 bool Outliner::IndentingPagesHdl( OutlinerView* pView )
1198 {
1199 if( !aIndentingPagesHdl.IsSet() )
1200 return true;
1201 return aIndentingPagesHdl.Call( pView );
1202 }
1203
ImpCanIndentSelectedPages(OutlinerView * pCurView)1204 bool Outliner::ImpCanIndentSelectedPages( OutlinerView* pCurView )
1205 {
1206 // The selected pages must already be set in advance through
1207 // ImpCalcSelectedPages
1208
1209 // If the first paragraph is on level 0 it can not indented in any case,
1210 // possible there might be indentations in the following on the 0 level.
1211 if ( ( mnFirstSelPage == 0 ) && ( GetOutlinerMode() != OutlinerMode::TextObject ) )
1212 {
1213 if ( nDepthChangedHdlPrevDepth == 1 ) // is the only page
1214 return false;
1215 else
1216 (void)pCurView->ImpCalcSelectedPages( false ); // without the first
1217 }
1218 return IndentingPagesHdl( pCurView );
1219 }
1220
1221
ImpCanDeleteSelectedPages(OutlinerView * pCurView)1222 bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView )
1223 {
1224 // The selected pages must already be set in advance through
1225 // ImpCalcSelectedPages
1226 return RemovingPagesHdl( pCurView );
1227 }
1228
Outliner(SfxItemPool * pPool,OutlinerMode nMode)1229 Outliner::Outliner(SfxItemPool* pPool, OutlinerMode nMode)
1230 : mnFirstSelPage(0)
1231 , nDepthChangedHdlPrevDepth(0)
1232 , nMaxDepth(9)
1233 , bFirstParaIsEmpty(true)
1234 , nBlockInsCallback(0)
1235 , bStrippingPortions(false)
1236 , bPasting(false)
1237 {
1238
1239 pParaList.reset( new ParagraphList );
1240 pParaList->SetVisibleStateChangedHdl( LINK( this, Outliner, ParaVisibleStateChangedHdl ) );
1241 std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
1242 pParaList->Append(std::move(pPara));
1243
1244 pEditEngine.reset( new OutlinerEditEng( this, pPool ) );
1245 pEditEngine->SetBeginMovingParagraphsHdl( LINK( this, Outliner, BeginMovingParagraphsHdl ) );
1246 pEditEngine->SetEndMovingParagraphsHdl( LINK( this, Outliner, EndMovingParagraphsHdl ) );
1247 pEditEngine->SetBeginPasteOrDropHdl( LINK( this, Outliner, BeginPasteOrDropHdl ) );
1248 pEditEngine->SetEndPasteOrDropHdl( LINK( this, Outliner, EndPasteOrDropHdl ) );
1249
1250 Init( nMode );
1251 }
1252
~Outliner()1253 Outliner::~Outliner()
1254 {
1255 pParaList->Clear();
1256 pParaList.reset();
1257 pEditEngine.reset();
1258 }
1259
InsertView(OutlinerView * pView,size_t nIndex)1260 size_t Outliner::InsertView( OutlinerView* pView, size_t nIndex )
1261 {
1262 size_t ActualIndex;
1263
1264 if ( nIndex >= aViewList.size() )
1265 {
1266 aViewList.push_back( pView );
1267 ActualIndex = aViewList.size() - 1;
1268 }
1269 else
1270 {
1271 ViewList::iterator it = aViewList.begin();
1272 advance( it, nIndex );
1273 ActualIndex = nIndex;
1274 }
1275 pEditEngine->InsertView( pView->pEditView.get(), nIndex );
1276 return ActualIndex;
1277 }
1278
RemoveView(OutlinerView const * pView)1279 void Outliner::RemoveView( OutlinerView const * pView )
1280 {
1281 ViewList::iterator it = std::find(aViewList.begin(), aViewList.end(), pView);
1282 if (it != aViewList.end())
1283 {
1284 pView->pEditView->HideCursor(); // HACK
1285 pEditEngine->RemoveView( pView->pEditView.get() );
1286 aViewList.erase( it );
1287 }
1288 }
1289
RemoveView(size_t nIndex)1290 void Outliner::RemoveView( size_t nIndex )
1291 {
1292 EditView* pEditView = pEditEngine->GetView( nIndex );
1293 pEditView->HideCursor(); // HACK
1294
1295 pEditEngine->RemoveView( nIndex );
1296
1297 {
1298 ViewList::iterator it = aViewList.begin();
1299 advance( it, nIndex );
1300 aViewList.erase( it );
1301 }
1302 }
1303
1304
GetView(size_t nIndex) const1305 OutlinerView* Outliner::GetView( size_t nIndex ) const
1306 {
1307 return ( nIndex >= aViewList.size() ) ? nullptr : aViewList[ nIndex ];
1308 }
1309
GetViewCount() const1310 size_t Outliner::GetViewCount() const
1311 {
1312 return aViewList.size();
1313 }
1314
ParagraphInsertedHdl(Paragraph * pPara)1315 void Outliner::ParagraphInsertedHdl(Paragraph* pPara)
1316 {
1317 if( !IsInUndo() )
1318 aParaInsertedHdl.Call( { this, pPara } );
1319 }
1320
1321
DepthChangedHdl(Paragraph * pPara,ParaFlag nPrevFlags)1322 void Outliner::DepthChangedHdl(Paragraph* pPara, ParaFlag nPrevFlags)
1323 {
1324 if( !IsInUndo() )
1325 aDepthChangedHdl.Call( { this, pPara, nPrevFlags } );
1326 }
1327
1328
GetAbsPos(Paragraph const * pPara) const1329 sal_Int32 Outliner::GetAbsPos( Paragraph const * pPara ) const
1330 {
1331 DBG_ASSERT(pPara,"GetAbsPos:No Para");
1332 return pParaList->GetAbsPos( pPara );
1333 }
1334
GetParagraphCount() const1335 sal_Int32 Outliner::GetParagraphCount() const
1336 {
1337 return pParaList->GetParagraphCount();
1338 }
1339
GetParagraph(sal_Int32 nAbsPos) const1340 Paragraph* Outliner::GetParagraph( sal_Int32 nAbsPos ) const
1341 {
1342 return pParaList->GetParagraph( nAbsPos );
1343 }
1344
HasChildren(Paragraph const * pParagraph) const1345 bool Outliner::HasChildren( Paragraph const * pParagraph ) const
1346 {
1347 return pParaList->HasChildren( pParagraph );
1348 }
1349
ImplHasNumberFormat(sal_Int32 nPara) const1350 bool Outliner::ImplHasNumberFormat( sal_Int32 nPara ) const
1351 {
1352 return GetNumberFormat(nPara) != nullptr;
1353 }
1354
GetNumberFormat(sal_Int32 nPara) const1355 const SvxNumberFormat* Outliner::GetNumberFormat( sal_Int32 nPara ) const
1356 {
1357 const SvxNumberFormat* pFmt = nullptr;
1358
1359 Paragraph* pPara = pParaList->GetParagraph( nPara );
1360 if (!pPara)
1361 return nullptr;
1362
1363 sal_Int16 nDepth = pPara->GetDepth();
1364
1365 if( nDepth >= 0 )
1366 {
1367 const SvxNumBulletItem& rNumBullet = pEditEngine->GetParaAttrib( nPara, EE_PARA_NUMBULLET );
1368 if ( rNumBullet.GetNumRule().GetLevelCount() > nDepth )
1369 pFmt = rNumBullet.GetNumRule().Get( nDepth );
1370 }
1371
1372 return pFmt;
1373 }
1374
ImplGetBulletSize(sal_Int32 nPara)1375 Size Outliner::ImplGetBulletSize( sal_Int32 nPara )
1376 {
1377 Paragraph* pPara = pParaList->GetParagraph( nPara );
1378 if (!pPara)
1379 return Size();
1380
1381 if( pPara->aBulSize.Width() == -1 )
1382 {
1383 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1384 assert(pFmt && "ImplGetBulletSize - no Bullet!");
1385
1386 if ( pFmt->GetNumberingType() == SVX_NUM_NUMBER_NONE )
1387 {
1388 pPara->aBulSize = Size( 0, 0 );
1389 }
1390 else if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
1391 {
1392 OUString aBulletText = ImplGetBulletText( nPara );
1393 OutputDevice* pRefDev = pEditEngine->GetRefDevice();
1394 vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
1395 vcl::Font aRefFont( pRefDev->GetFont());
1396 pRefDev->SetFont( aBulletFont );
1397 pPara->aBulSize.setWidth( pRefDev->GetTextWidth( aBulletText ) );
1398 pPara->aBulSize.setHeight( pRefDev->GetTextHeight() );
1399 pRefDev->SetFont( aRefFont );
1400 }
1401 else
1402 {
1403 pPara->aBulSize = OutputDevice::LogicToLogic(pFmt->GetGraphicSize(),
1404 MapMode(MapUnit::Map100thMM),
1405 pEditEngine->GetRefDevice()->GetMapMode());
1406 }
1407 }
1408
1409 return pPara->aBulSize;
1410 }
1411
ImplCheckParagraphs(sal_Int32 nStart,sal_Int32 nEnd)1412 void Outliner::ImplCheckParagraphs( sal_Int32 nStart, sal_Int32 nEnd )
1413 {
1414
1415 for ( sal_Int32 n = nStart; n < nEnd; n++ )
1416 {
1417 Paragraph* pPara = pParaList->GetParagraph( n );
1418 if (pPara)
1419 {
1420 pPara->Invalidate();
1421 ImplCalcBulletText( n, false, false );
1422 }
1423 }
1424 }
1425
SetRefDevice(OutputDevice * pRefDev)1426 void Outliner::SetRefDevice( OutputDevice* pRefDev )
1427 {
1428 pEditEngine->SetRefDevice( pRefDev );
1429 for ( sal_Int32 n = pParaList->GetParagraphCount(); n; )
1430 {
1431 Paragraph* pPara = pParaList->GetParagraph( --n );
1432 pPara->Invalidate();
1433 }
1434 }
1435
ParaAttribsChanged(sal_Int32 nPara)1436 void Outliner::ParaAttribsChanged( sal_Int32 nPara )
1437 {
1438 // The Outliner does not have an undo of its own, when paragraphs are
1439 // separated/merged. When ParagraphInserted the attribute EE_PARA_OUTLLEVEL
1440 // may not be set, this is however needed when the depth of the paragraph
1441 // is to be determined.
1442 if (!pEditEngine->IsInUndo())
1443 return;
1444 if (pParaList->GetParagraphCount() != pEditEngine->GetParagraphCount())
1445 return;
1446 Paragraph* pPara = pParaList->GetParagraph(nPara);
1447 if (!pPara)
1448 return;
1449 // tdf#100734: force update of bullet
1450 pPara->Invalidate();
1451 const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
1452 if (pPara->GetDepth() == rLevel.GetValue())
1453 return;
1454 pPara->SetDepth(rLevel.GetValue());
1455 ImplCalcBulletText(nPara, true, true);
1456 }
1457
StyleSheetChanged(SfxStyleSheet const * pStyle)1458 void Outliner::StyleSheetChanged( SfxStyleSheet const * pStyle )
1459 {
1460
1461 // The EditEngine calls StyleSheetChanged also for derived styles.
1462 // Here all the paragraphs, which had the said template, used to be
1463 // hunted by an ImpRecalcParaAttribs, why?
1464 // => only the Bullet-representation can really change...
1465 sal_Int32 nParas = pParaList->GetParagraphCount();
1466 for( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
1467 {
1468 if ( pEditEngine->GetStyleSheet( nPara ) == pStyle )
1469 {
1470 ImplCheckNumBulletItem( nPara );
1471 ImplCalcBulletText( nPara, false, false );
1472 // EditEngine formats changed paragraphs before calling this method,
1473 // so they are not reformatted now and use wrong bullet indent
1474 pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
1475 }
1476 }
1477 }
1478
ImpCalcBulletArea(sal_Int32 nPara,bool bAdjust,bool bReturnPaperPos)1479 tools::Rectangle Outliner::ImpCalcBulletArea( sal_Int32 nPara, bool bAdjust, bool bReturnPaperPos )
1480 {
1481 // Bullet area within the paragraph ...
1482 tools::Rectangle aBulletArea;
1483
1484 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1485 if ( pFmt )
1486 {
1487 Point aTopLeft;
1488 Size aBulletSize( ImplGetBulletSize( nPara ) );
1489
1490 bool bOutlineMode = bool( pEditEngine->GetControlWord() & EEControlBits::OUTLINER );
1491
1492 // the ODF attribute text:space-before which holds the spacing to add to the left of the label
1493 const auto nSpaceBefore = pFmt->GetAbsLSpace() + pFmt->GetFirstLineOffset();
1494
1495 const SvxLRSpaceItem& rLR = pEditEngine->GetParaAttrib( nPara, bOutlineMode ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
1496 aTopLeft.setX( rLR.GetTextLeft() + rLR.GetTextFirstLineOffset() + nSpaceBefore );
1497
1498 tools::Long nBulletWidth = std::max( static_cast<tools::Long>(-rLR.GetTextFirstLineOffset()), static_cast<tools::Long>((-pFmt->GetFirstLineOffset()) + pFmt->GetCharTextDistance()) );
1499 if ( nBulletWidth < aBulletSize.Width() ) // The Bullet creates its space
1500 nBulletWidth = aBulletSize.Width();
1501
1502 if ( bAdjust && !bOutlineMode )
1503 {
1504 // Adjust when centered or align right
1505 const SvxAdjustItem& rItem = pEditEngine->GetParaAttrib( nPara, EE_PARA_JUST );
1506 if ( ( !pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Left ) ) ||
1507 ( pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Right ) ) )
1508 {
1509 aTopLeft.setX( pEditEngine->GetFirstLineStartX( nPara ) - nBulletWidth );
1510 }
1511 }
1512
1513 // Vertical:
1514 ParagraphInfos aInfos = pEditEngine->GetParagraphInfos( nPara );
1515 if ( aInfos.bValid )
1516 {
1517 aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintBullet) from the EditEngine
1518 aInfos.nFirstLineHeight - aInfos.nFirstLineTextHeight
1519 + aInfos.nFirstLineTextHeight / 2
1520 - aBulletSize.Height() / 2 );
1521 // may prefer to print out on the baseline ...
1522 if( ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
1523 {
1524 vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
1525 if ( aBulletFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL )
1526 {
1527 OutputDevice* pRefDev = pEditEngine->GetRefDevice();
1528 vcl::Font aOldFont = pRefDev->GetFont();
1529 pRefDev->SetFont( aBulletFont );
1530 FontMetric aMetric( pRefDev->GetFontMetric() );
1531 // Leading on the first line ...
1532 aTopLeft.setY( /* aInfos.nFirstLineOffset + */ aInfos.nFirstLineMaxAscent );
1533 aTopLeft.AdjustY( -(aMetric.GetAscent()) );
1534 pRefDev->SetFont( aOldFont );
1535 }
1536 }
1537 }
1538
1539 // Horizontal:
1540 if( pFmt->GetNumAdjust() == SvxAdjust::Right )
1541 {
1542 aTopLeft.AdjustX(nBulletWidth - aBulletSize.Width() );
1543 }
1544 else if( pFmt->GetNumAdjust() == SvxAdjust::Center )
1545 {
1546 aTopLeft.AdjustX(( nBulletWidth - aBulletSize.Width() ) / 2 );
1547 }
1548
1549 if ( aTopLeft.X() < 0 ) // then push
1550 aTopLeft.setX( 0 );
1551
1552 aBulletArea = tools::Rectangle( aTopLeft, aBulletSize );
1553 }
1554 if ( bReturnPaperPos )
1555 {
1556 Size aBulletSize( aBulletArea.GetSize() );
1557 Point aBulletDocPos( aBulletArea.TopLeft() );
1558 aBulletDocPos.AdjustY(pEditEngine->GetDocPosTopLeft( nPara ).Y() );
1559 Point aBulletPos( aBulletDocPos );
1560
1561 if ( IsVertical() )
1562 {
1563 aBulletPos.setY( aBulletDocPos.X() );
1564 aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.Y() );
1565 // Rotate:
1566 aBulletPos.AdjustX( -(aBulletSize.Height()) );
1567 Size aSz( aBulletSize );
1568 aBulletSize.setWidth( aSz.Height() );
1569 aBulletSize.setHeight( aSz.Width() );
1570 }
1571 else if ( pEditEngine->IsRightToLeft( nPara ) )
1572 {
1573 aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.X() - aBulletSize.Width() );
1574 }
1575
1576 aBulletArea = tools::Rectangle( aBulletPos, aBulletSize );
1577 }
1578 return aBulletArea;
1579 }
1580
GetBulletInfo(sal_Int32 nPara)1581 EBulletInfo Outliner::GetBulletInfo( sal_Int32 nPara )
1582 {
1583 EBulletInfo aInfo;
1584
1585 aInfo.nParagraph = nPara;
1586 aInfo.bVisible = ImplHasNumberFormat( nPara );
1587
1588 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1589 aInfo.nType = pFmt ? pFmt->GetNumberingType() : 0;
1590
1591 if( pFmt )
1592 {
1593 if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
1594 {
1595 aInfo.aText = ImplGetBulletText( nPara );
1596
1597 if( pFmt->GetBulletFont() )
1598 aInfo.aFont = *pFmt->GetBulletFont();
1599 }
1600 }
1601
1602 if ( aInfo.bVisible )
1603 {
1604 aInfo.aBounds = ImpCalcBulletArea( nPara, true, true );
1605 }
1606
1607 return aInfo;
1608 }
1609
GetText(Paragraph const * pParagraph,sal_Int32 nCount) const1610 OUString Outliner::GetText( Paragraph const * pParagraph, sal_Int32 nCount ) const
1611 {
1612
1613 OUStringBuffer aText(128);
1614 sal_Int32 nStartPara = pParaList->GetAbsPos( pParagraph );
1615 for ( sal_Int32 n = 0; n < nCount; n++ )
1616 {
1617 aText.append(pEditEngine->GetText( nStartPara + n ));
1618 if ( (n+1) < nCount )
1619 aText.append("\n");
1620 }
1621 return aText.makeStringAndClear();
1622 }
1623
Remove(Paragraph const * pPara,sal_Int32 nParaCount)1624 void Outliner::Remove( Paragraph const * pPara, sal_Int32 nParaCount )
1625 {
1626
1627 sal_Int32 nPos = pParaList->GetAbsPos( pPara );
1628 if( !nPos && ( nParaCount >= pParaList->GetParagraphCount() ) )
1629 {
1630 Clear();
1631 }
1632 else
1633 {
1634 for( sal_Int32 n = 0; n < nParaCount; n++ )
1635 pEditEngine->RemoveParagraph( nPos );
1636 }
1637 }
1638
StripPortions()1639 void Outliner::StripPortions()
1640 {
1641 bStrippingPortions = true;
1642 pEditEngine->StripPortions();
1643 bStrippingPortions = false;
1644 }
1645
DrawingText(const Point & rStartPos,const OUString & rText,sal_Int32 nTextStart,sal_Int32 nTextLen,std::span<const sal_Int32> pDXArray,std::span<const sal_Bool> pKashidaArray,const SvxFont & rFont,sal_Int32 nPara,sal_uInt8 nRightToLeft,const EEngineData::WrongSpellVector * pWrongSpellVector,const SvxFieldData * pFieldData,bool bEndOfLine,bool bEndOfParagraph,bool bEndOfBullet,const css::lang::Locale * pLocale,const Color & rOverlineColor,const Color & rTextLineColor)1646 void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart,
1647 sal_Int32 nTextLen, std::span<const sal_Int32> pDXArray,
1648 std::span<const sal_Bool> pKashidaArray, const SvxFont& rFont,
1649 sal_Int32 nPara, sal_uInt8 nRightToLeft,
1650 const EEngineData::WrongSpellVector* pWrongSpellVector,
1651 const SvxFieldData* pFieldData,
1652 bool bEndOfLine,
1653 bool bEndOfParagraph,
1654 bool bEndOfBullet,
1655 const css::lang::Locale* pLocale,
1656 const Color& rOverlineColor,
1657 const Color& rTextLineColor)
1658 {
1659 if(aDrawPortionHdl.IsSet())
1660 {
1661 DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, nPara, pDXArray, pKashidaArray, pWrongSpellVector,
1662 pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft, false, 0, bEndOfLine, bEndOfParagraph, bEndOfBullet);
1663
1664 aDrawPortionHdl.Call( &aInfo );
1665 }
1666 }
1667
DrawingTab(const Point & rStartPos,tools::Long nWidth,const OUString & rChar,const SvxFont & rFont,sal_Int32 nPara,sal_uInt8 nRightToLeft,bool bEndOfLine,bool bEndOfParagraph,const Color & rOverlineColor,const Color & rTextLineColor)1668 void Outliner::DrawingTab( const Point& rStartPos, tools::Long nWidth, const OUString& rChar, const SvxFont& rFont,
1669 sal_Int32 nPara, sal_uInt8 nRightToLeft, bool bEndOfLine, bool bEndOfParagraph,
1670 const Color& rOverlineColor, const Color& rTextLineColor)
1671 {
1672 if(aDrawPortionHdl.IsSet())
1673 {
1674 DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, nPara, {}, {}, nullptr,
1675 nullptr, nullptr, rOverlineColor, rTextLineColor, nRightToLeft, true, nWidth, bEndOfLine, bEndOfParagraph, false);
1676
1677 aDrawPortionHdl.Call( &aInfo );
1678 }
1679 }
1680
RemovingPagesHdl(OutlinerView * pView)1681 bool Outliner::RemovingPagesHdl( OutlinerView* pView )
1682 {
1683 return !aRemovingPagesHdl.IsSet() || aRemovingPagesHdl.Call( pView );
1684 }
1685
ImpCanDeleteSelectedPages(OutlinerView * pCurView,sal_Int32 _nFirstPage,sal_Int32 nPages)1686 bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView, sal_Int32 _nFirstPage, sal_Int32 nPages )
1687 {
1688
1689 nDepthChangedHdlPrevDepth = nPages;
1690 mnFirstSelPage = _nFirstPage;
1691 return RemovingPagesHdl( pCurView );
1692 }
1693
GetParaAttribs(sal_Int32 nPara) const1694 SfxItemSet const & Outliner::GetParaAttribs( sal_Int32 nPara ) const
1695 {
1696 return pEditEngine->GetParaAttribs( nPara );
1697 }
1698
IMPL_LINK(Outliner,ParaVisibleStateChangedHdl,Paragraph &,rPara,void)1699 IMPL_LINK( Outliner, ParaVisibleStateChangedHdl, Paragraph&, rPara, void )
1700 {
1701 sal_Int32 nPara = pParaList->GetAbsPos( &rPara );
1702 pEditEngine->ShowParagraph( nPara, rPara.IsVisible() );
1703 }
1704
IMPL_LINK_NOARG(Outliner,BeginMovingParagraphsHdl,MoveParagraphsInfo &,void)1705 IMPL_LINK_NOARG(Outliner, BeginMovingParagraphsHdl, MoveParagraphsInfo&, void)
1706 {
1707 if( !IsInUndo() )
1708 aBeginMovingHdl.Call( this );
1709 }
1710
IMPL_LINK(Outliner,BeginPasteOrDropHdl,PasteOrDropInfos &,rInfos,void)1711 IMPL_LINK( Outliner, BeginPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
1712 {
1713 UndoActionStart( EDITUNDO_DRAGANDDROP );
1714 maBeginPasteOrDropHdl.Call(&rInfos);
1715 }
1716
IMPL_LINK(Outliner,EndPasteOrDropHdl,PasteOrDropInfos &,rInfos,void)1717 IMPL_LINK( Outliner, EndPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
1718 {
1719 bPasting = false;
1720 ImpTextPasted( rInfos.nStartPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
1721 maEndPasteOrDropHdl.Call( &rInfos );
1722 UndoActionEnd();
1723 }
1724
IMPL_LINK(Outliner,EndMovingParagraphsHdl,MoveParagraphsInfo &,rInfos,void)1725 IMPL_LINK( Outliner, EndMovingParagraphsHdl, MoveParagraphsInfo&, rInfos, void )
1726 {
1727 pParaList->MoveParagraphs( rInfos.nStartPara, rInfos.nDestPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
1728 sal_Int32 nChangesStart = std::min( rInfos.nStartPara, rInfos.nDestPara );
1729 sal_Int32 nParas = pParaList->GetParagraphCount();
1730 for ( sal_Int32 n = nChangesStart; n < nParas; n++ )
1731 ImplCalcBulletText( n, false, false );
1732
1733 if( !IsInUndo() )
1734 aEndMovingHdl.Call( this );
1735 }
1736
isSameNumbering(const SvxNumberFormat & rN1,const SvxNumberFormat & rN2)1737 static bool isSameNumbering( const SvxNumberFormat& rN1, const SvxNumberFormat& rN2 )
1738 {
1739 if( rN1.GetNumberingType() != rN2.GetNumberingType() )
1740 return false;
1741
1742 if( rN1.GetNumStr(1) != rN2.GetNumStr(1) )
1743 return false;
1744
1745 if( (rN1.GetPrefix() != rN2.GetPrefix()) || (rN1.GetSuffix() != rN2.GetSuffix()) )
1746 return false;
1747
1748 return true;
1749 }
1750
ImplGetNumbering(sal_Int32 nPara,const SvxNumberFormat * pParaFmt)1751 sal_uInt16 Outliner::ImplGetNumbering( sal_Int32 nPara, const SvxNumberFormat* pParaFmt )
1752 {
1753 sal_uInt16 nNumber = pParaFmt->GetStart() - 1;
1754
1755 Paragraph* pPara = pParaList->GetParagraph( nPara );
1756 const sal_Int16 nParaDepth = pPara->GetDepth();
1757
1758 do
1759 {
1760 pPara = pParaList->GetParagraph( nPara );
1761 const sal_Int16 nDepth = pPara->GetDepth();
1762
1763 // ignore paragraphs that are below our paragraph or have no numbering
1764 if( (nDepth > nParaDepth) || (nDepth == -1) )
1765 continue;
1766
1767 // stop on paragraphs that are above our paragraph
1768 if( nDepth < nParaDepth )
1769 break;
1770
1771 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1772
1773 if( pFmt == nullptr )
1774 continue; // ignore paragraphs without bullets
1775
1776 // check if numbering less than or equal to pParaFmt
1777 if( !isSameNumbering( *pFmt, *pParaFmt ) || ( pFmt->GetStart() < pParaFmt->GetStart() ) )
1778 break;
1779
1780 if ( pFmt->GetStart() > pParaFmt->GetStart() )
1781 {
1782 nNumber += pFmt->GetStart() - pParaFmt->GetStart();
1783 pParaFmt = pFmt;
1784 }
1785
1786 const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
1787
1788 if( rBulletState.GetValue() )
1789 nNumber += 1;
1790
1791 // same depth, same number format, check for restart
1792 const sal_Int16 nNumberingStartValue = pPara->GetNumberingStartValue();
1793 if( (nNumberingStartValue != -1) || pPara->IsParaIsNumberingRestart() )
1794 {
1795 if( nNumberingStartValue != -1 )
1796 nNumber += nNumberingStartValue - 1;
1797 break;
1798 }
1799 }
1800 while( nPara-- );
1801
1802 return nNumber;
1803 }
1804
ImplCalcBulletText(sal_Int32 nPara,bool bRecalcLevel,bool bRecalcChildren)1805 void Outliner::ImplCalcBulletText( sal_Int32 nPara, bool bRecalcLevel, bool bRecalcChildren )
1806 {
1807
1808 Paragraph* pPara = pParaList->GetParagraph( nPara );
1809
1810 while ( pPara )
1811 {
1812 OUString aBulletText;
1813 const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
1814 if( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) )
1815 {
1816 aBulletText += pFmt->GetPrefix();
1817 if( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
1818 {
1819 sal_UCS4 cChar = pFmt->GetBulletChar();
1820 aBulletText += OUString(&cChar, 1);
1821 }
1822 else if( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE )
1823 {
1824 aBulletText += pFmt->GetNumStr( ImplGetNumbering( nPara, pFmt ) );
1825 }
1826 aBulletText += pFmt->GetSuffix();
1827 }
1828
1829 if (pPara->GetText() != aBulletText)
1830 pPara->SetText( aBulletText );
1831
1832 if ( bRecalcLevel )
1833 {
1834 sal_Int16 nDepth = pPara->GetDepth();
1835 pPara = pParaList->GetParagraph( ++nPara );
1836 if ( !bRecalcChildren )
1837 {
1838 while ( pPara && ( pPara->GetDepth() > nDepth ) )
1839 pPara = pParaList->GetParagraph( ++nPara );
1840 }
1841
1842 if ( pPara && ( pPara->GetDepth() < nDepth ) )
1843 pPara = nullptr;
1844 }
1845 else
1846 {
1847 pPara = nullptr;
1848 }
1849 }
1850 }
1851
Clear()1852 void Outliner::Clear()
1853 {
1854
1855 if( !bFirstParaIsEmpty )
1856 {
1857 ImplBlockInsertionCallbacks( true );
1858 pEditEngine->Clear();
1859 pParaList->Clear();
1860 pParaList->Append( std::unique_ptr<Paragraph>(new Paragraph( gnMinDepth )));
1861 bFirstParaIsEmpty = true;
1862 ImplBlockInsertionCallbacks( false );
1863 }
1864 else
1865 {
1866 Paragraph* pPara = pParaList->GetParagraph( 0 );
1867 if(pPara)
1868 pPara->SetDepth( gnMinDepth );
1869 }
1870 }
1871
SetFlatMode(bool bFlat)1872 void Outliner::SetFlatMode( bool bFlat )
1873 {
1874
1875 if( bFlat != pEditEngine->IsFlatMode() )
1876 {
1877 for ( sal_Int32 nPara = pParaList->GetParagraphCount(); nPara; )
1878 pParaList->GetParagraph( --nPara )->aBulSize.setWidth( -1 );
1879
1880 pEditEngine->SetFlatMode( bFlat );
1881 }
1882 }
1883
ImplGetBulletText(sal_Int32 nPara)1884 OUString Outliner::ImplGetBulletText( sal_Int32 nPara )
1885 {
1886 OUString aRes;
1887 Paragraph* pPara = pParaList->GetParagraph( nPara );
1888 if (pPara)
1889 {
1890 ImplCalcBulletText( nPara, false, false );
1891 aRes = pPara->GetText();
1892 }
1893 return aRes;
1894 }
1895
1896 // this is needed for StarOffice Api
SetLevelDependentStyleSheet(sal_Int32 nPara)1897 void Outliner::SetLevelDependentStyleSheet( sal_Int32 nPara )
1898 {
1899 SfxItemSet aOldAttrs( pEditEngine->GetParaAttribs( nPara ) );
1900 ImplSetLevelDependentStyleSheet( nPara );
1901 pEditEngine->SetParaAttribs( nPara, aOldAttrs );
1902 }
1903
ImplBlockInsertionCallbacks(bool b)1904 void Outliner::ImplBlockInsertionCallbacks( bool b )
1905 {
1906 if ( b )
1907 {
1908 nBlockInsCallback++;
1909 }
1910 else
1911 {
1912 DBG_ASSERT( nBlockInsCallback, "ImplBlockInsertionCallbacks ?!" );
1913 nBlockInsCallback--;
1914 if ( !nBlockInsCallback )
1915 {
1916 // Call blocked notify events...
1917 while(!pEditEngine->aNotifyCache.empty())
1918 {
1919 EENotify aNotify(pEditEngine->aNotifyCache.front());
1920 // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
1921 pEditEngine->aNotifyCache.erase(pEditEngine->aNotifyCache.begin());
1922 pEditEngine->aOutlinerNotifyHdl.Call( aNotify );
1923 }
1924 }
1925 }
1926 }
1927
IMPL_LINK(Outliner,EditEngineNotifyHdl,EENotify &,rNotify,void)1928 IMPL_LINK( Outliner, EditEngineNotifyHdl, EENotify&, rNotify, void )
1929 {
1930 if ( !nBlockInsCallback )
1931 pEditEngine->aOutlinerNotifyHdl.Call( rNotify );
1932 else
1933 pEditEngine->aNotifyCache.push_back(rNotify);
1934 }
1935
1936 /** sets a link that is called at the beginning of a drag operation at an edit view */
SetBeginDropHdl(const Link<EditView *,void> & rLink)1937 void Outliner::SetBeginDropHdl( const Link<EditView*,void>& rLink )
1938 {
1939 pEditEngine->SetBeginDropHdl( rLink );
1940 }
1941
1942 /** sets a link that is called at the end of a drag operation at an edit view */
SetEndDropHdl(const Link<EditView *,void> & rLink)1943 void Outliner::SetEndDropHdl( const Link<EditView*,void>& rLink )
1944 {
1945 pEditEngine->SetEndDropHdl( rLink );
1946 }
1947
1948 /** sets a link that is called before a drop or paste operation. */
SetBeginPasteOrDropHdl(const Link<PasteOrDropInfos *,void> & rLink)1949 void Outliner::SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
1950 {
1951 maBeginPasteOrDropHdl = rLink;
1952 }
1953
1954 /** sets a link that is called after a drop or paste operation. */
SetEndPasteOrDropHdl(const Link<PasteOrDropInfos *,void> & rLink)1955 void Outliner::SetEndPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
1956 {
1957 maEndPasteOrDropHdl = rLink;
1958 }
1959
SetParaFlag(Paragraph * pPara,ParaFlag nFlag)1960 void Outliner::SetParaFlag( Paragraph* pPara, ParaFlag nFlag )
1961 {
1962 if( pPara && !pPara->HasFlag( nFlag ) )
1963 {
1964 if( IsUndoEnabled() && !IsInUndo() )
1965 InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( this, GetAbsPos( pPara ), pPara->nFlags, pPara->nFlags|nFlag ) );
1966
1967 pPara->SetFlag( nFlag );
1968 }
1969 }
1970
HasParaFlag(const Paragraph * pPara,ParaFlag nFlag)1971 bool Outliner::HasParaFlag( const Paragraph* pPara, ParaFlag nFlag )
1972 {
1973 return pPara && pPara->HasFlag( nFlag );
1974 }
1975
1976
IsPageOverflow()1977 bool Outliner::IsPageOverflow()
1978 {
1979 return pEditEngine->IsPageOverflow();
1980 }
1981
GetNonOverflowingText() const1982 std::optional<NonOverflowingText> Outliner::GetNonOverflowingText() const
1983 {
1984 /* XXX:
1985 * nCount should be the number of paragraphs of the non overflowing text
1986 * nStart should be the starting paragraph of the non overflowing text (XXX: Always 0?)
1987 */
1988
1989 if ( GetParagraphCount() < 1 )
1990 return {};
1991
1992 // last non-overflowing paragraph is before the first overflowing one
1993 sal_Int32 nCount = pEditEngine->GetOverflowingParaNum();
1994 sal_Int32 nOverflowLine = pEditEngine->GetOverflowingLineNum(); // XXX: Unused for now
1995
1996 // Defensive check: overflowing para index beyond actual # of paragraphs?
1997 if ( nCount > GetParagraphCount()-1) {
1998 SAL_INFO("editeng.chaining",
1999 "[Overflowing] Ops, trying to retrieve para "
2000 << nCount << " when max index is " << GetParagraphCount()-1 );
2001 return {};
2002 }
2003
2004 if (nCount < 0)
2005 {
2006 SAL_INFO("editeng.chaining",
2007 "[Overflowing] No Overflowing text but GetNonOverflowinText called?!");
2008 return {};
2009 }
2010
2011 // NOTE: We want the selection of the overflowing text from here
2012 // At the same time we may want to consider the beginning of such text
2013 // in a more fine grained way (i.e. as GetNonOverflowingText did)
2014
2015 /*
2016 sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
2017 sal_uInt32 nParaCount = GetParagraphCount();
2018
2019 sal_uInt32 nLen = 0;
2020 for ( sal_Int32 nLine = 0;
2021 nLine < pEditEngine->GetOverflowingLineNum();
2022 nLine++) {
2023 nLen += GetLineLen(nHeadPara, nLine);
2024 }
2025
2026 sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
2027 ESelection aOverflowingTextSel;
2028 sal_Int32 nLastPara = nParaCount-1;
2029 sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
2030 aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
2031 nLastPara, nLastParaLen);
2032 bool bLastParaInterrupted =
2033 pEditEngine->GetOverflowingLineNum() > 0;
2034
2035 return new NonOverflowingText(aOverflowingTextSel, bLastParaInterrupted);
2036 **/
2037
2038
2039 // Only overflowing text, i.e. 1st line of 1st paragraph overflowing
2040 bool bItAllOverflew = nCount == 0 && nOverflowLine == 0;
2041 if ( bItAllOverflew )
2042 {
2043 ESelection aEmptySel(0,0,0,0);
2044 //EditTextObject *pTObj = pEditEngine->CreateTextObject(aEmptySel);
2045 bool const bLastParaInterrupted = true; // Last Para was interrupted since everything overflew
2046 return NonOverflowingText(aEmptySel, bLastParaInterrupted);
2047 } else { // Get the lines that of the overflowing para fit in the box
2048
2049 sal_Int32 nOverflowingPara = nCount;
2050 sal_uInt32 nLen = 0;
2051
2052 for ( sal_Int32 nLine = 0;
2053 nLine < pEditEngine->GetOverflowingLineNum();
2054 nLine++)
2055 {
2056 nLen += GetLineLen(nOverflowingPara, nLine);
2057 }
2058
2059 //sal_Int32 nStartPara = 0;
2060 //sal_Int32 nStartPos = 0;
2061 ESelection aOverflowingTextSelection;
2062
2063 const sal_Int32 nEndPara = GetParagraphCount()-1;
2064 const sal_Int32 nEndPos = pEditEngine->GetTextLen(nEndPara);
2065
2066 if (nLen == 0) {
2067 // XXX: What happens inside this case might be dependent on the joining paragraph or not-thingy
2068 // Overflowing paragraph is empty or first line overflowing: it's not "Non-Overflowing" text then
2069 sal_Int32 nParaLen = GetText(GetParagraph(nOverflowingPara-1)).getLength();
2070 aOverflowingTextSelection =
2071 ESelection(nOverflowingPara-1, nParaLen, nEndPara, nEndPos);
2072 } else {
2073 // We take until we have to from the overflowing paragraph
2074 aOverflowingTextSelection =
2075 ESelection(nOverflowingPara, nLen, nEndPara, nEndPos);
2076 }
2077 //EditTextObject *pTObj = pEditEngine->CreateTextObject(aNonOverflowingTextSelection);
2078
2079 //sal_Int32 nLastLine = GetLineCount(nOverflowingPara)-1;
2080 bool bLastParaInterrupted =
2081 pEditEngine->GetOverflowingLineNum() > 0;
2082
2083 return NonOverflowingText(aOverflowingTextSelection, bLastParaInterrupted);
2084 }
2085 }
2086
GetEmptyParaObject() const2087 OutlinerParaObject Outliner::GetEmptyParaObject() const
2088 {
2089 std::unique_ptr<EditTextObject> pEmptyText = pEditEngine->GetEmptyTextObject();
2090 OutlinerParaObject aPObj( std::move(pEmptyText) );
2091 aPObj.SetOutlinerMode(GetOutlinerMode());
2092 return aPObj;
2093 }
2094
GetOverflowingText() const2095 std::optional<OverflowingText> Outliner::GetOverflowingText() const
2096 {
2097 if ( pEditEngine->GetOverflowingParaNum() < 0)
2098 return {};
2099
2100
2101 // Defensive check: overflowing para index beyond actual # of paragraphs?
2102 if ( pEditEngine->GetOverflowingParaNum() > GetParagraphCount()-1) {
2103 SAL_INFO("editeng.chaining",
2104 "[Overflowing] Ops, trying to retrieve para "
2105 << pEditEngine->GetOverflowingParaNum() << " when max index is "
2106 << GetParagraphCount()-1 );
2107 return {};
2108 }
2109
2110 sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
2111 sal_uInt32 nParaCount = GetParagraphCount();
2112
2113 sal_uInt32 nLen = 0;
2114 for ( sal_Int32 nLine = 0;
2115 nLine < pEditEngine->GetOverflowingLineNum();
2116 nLine++) {
2117 nLen += GetLineLen(nHeadPara, nLine);
2118 }
2119
2120 sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
2121 ESelection aOverflowingTextSel;
2122 sal_Int32 nLastPara = nParaCount-1;
2123 sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
2124 aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
2125 nLastPara, nLastParaLen);
2126 return OverflowingText(pEditEngine->CreateTransferable(aOverflowingTextSel));
2127
2128 }
2129
ClearOverflowingParaNum()2130 void Outliner::ClearOverflowingParaNum()
2131 {
2132 pEditEngine->ClearOverflowingParaNum();
2133 }
2134
dumpAsXml(xmlTextWriterPtr pWriter) const2135 void Outliner::dumpAsXml(xmlTextWriterPtr pWriter) const
2136 {
2137 bool bOwns = false;
2138 if (!pWriter)
2139 {
2140 pWriter = xmlNewTextWriterFilename("outliner.xml", 0);
2141 xmlTextWriterSetIndent(pWriter,1);
2142 (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
2143 (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
2144 bOwns = true;
2145 }
2146
2147 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Outliner"));
2148 pParaList->dumpAsXml(pWriter);
2149 (void)xmlTextWriterEndElement(pWriter);
2150
2151 if (bOwns)
2152 {
2153 (void)xmlTextWriterEndDocument(pWriter);
2154 xmlFreeTextWriter(pWriter);
2155 }
2156 }
2157
2158 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2159