xref: /core/sc/source/ui/view/output.cxx (revision 9e3cebf6c017b01ec917c818988db45aa9ac0b1d)
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 <scitems.hxx>
21 #include <editeng/brushitem.hxx>
22 #include <svtools/colorcfg.hxx>
23 #include <svx/rotmodit.hxx>
24 #include <svx/svdocapt.hxx>
25 #include <editeng/shaditem.hxx>
26 #include <editeng/svxfont.hxx>
27 #include <tools/poly.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/pdfextoutdevdata.hxx>
30 #include <svx/framelinkarray.hxx>
31 #include <drawinglayer/geometry/viewinformation2d.hxx>
32 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
33 #include <drawinglayer/processor2d/processor2dtools.hxx>
34 #include <officecfg/Office/Common.hxx>
35 #include <officecfg/Office/Calc.hxx>
36 #include <vcl/lineinfo.hxx>
37 #include <vcl/gradient.hxx>
38 #include <vcl/settings.hxx>
39 #include <vcl/pdf/PDFNote.hxx>
40 #include <svx/unoapi.hxx>
41 #include <sal/log.hxx>
42 #include <comphelper/lok.hxx>
43 #include <o3tl/unit_conversion.hxx>
44 #include <basegfx/matrix/b2dhommatrix.hxx>
45 
46 #include <output.hxx>
47 #include <document.hxx>
48 #include <drwlayer.hxx>
49 #include <formulacell.hxx>
50 #include <attrib.hxx>
51 #include <patattr.hxx>
52 #include <progress.hxx>
53 #include <pagedata.hxx>
54 #include <chgtrack.hxx>
55 #include <chgviset.hxx>
56 #include <viewutil.hxx>
57 #include <gridmerg.hxx>
58 #include <fillinfo.hxx>
59 #include <scmod.hxx>
60 #include <appoptio.hxx>
61 #include <postit.hxx>
62 #include <validat.hxx>
63 #include <detfunc.hxx>
64 #include <editutil.hxx>
65 
66 #include <SparklineRenderer.hxx>
67 #include <colorscale.hxx>
68 
69 #include <math.h>
70 #include <memory>
71 
72 using namespace com::sun::star;
73 
74 // Static Data
75 
76 // color for ChangeTracking "by author" as in the writer (swmodul1.cxx)
77 
78 #define SC_AUTHORCOLORCOUNT     9
79 
80 const Color nAuthorColor[ SC_AUTHORCOLORCOUNT ] = {
81                     COL_LIGHTRED,       COL_LIGHTBLUE,      COL_LIGHTMAGENTA,
82                     COL_GREEN,          COL_RED,            COL_BLUE,
83                     COL_BROWN,          COL_MAGENTA,        COL_CYAN };
84 
85 // Helper class for color assignment to avoid repeated lookups for the same user
86 
ScActionColorChanger(const ScChangeTrack & rTrack)87 ScActionColorChanger::ScActionColorChanger( const ScChangeTrack& rTrack ) :
88     rOpt( ScModule::get()->GetAppOptions() ),
89     rUsers( rTrack.GetUserCollection() ),
90     nLastUserIndex( 0 ),
91     nColor( COL_BLACK )
92 {
93 }
94 
Update(const ScChangeAction & rAction)95 void ScActionColorChanger::Update( const ScChangeAction& rAction )
96 {
97     Color nSetColor;
98     switch (rAction.GetType())
99     {
100         case SC_CAT_INSERT_COLS:
101         case SC_CAT_INSERT_ROWS:
102         case SC_CAT_INSERT_TABS:
103             nSetColor = rOpt.GetTrackInsertColor();
104             break;
105         case SC_CAT_DELETE_COLS:
106         case SC_CAT_DELETE_ROWS:
107         case SC_CAT_DELETE_TABS:
108             nSetColor = rOpt.GetTrackDeleteColor();
109             break;
110         case SC_CAT_MOVE:
111             nSetColor = rOpt.GetTrackMoveColor();
112             break;
113         default:
114             nSetColor = rOpt.GetTrackContentColor();
115             break;
116     }
117     if ( nSetColor != COL_TRANSPARENT )     // color assigned
118         nColor = nSetColor;
119     else                                    // by author
120     {
121         if (aLastUserName != rAction.GetUser())
122         {
123             aLastUserName = rAction.GetUser();
124             std::set<OUString>::const_iterator it = rUsers.find(aLastUserName);
125             if (it == rUsers.end())
126             {
127                 // empty string is possible if a name wasn't found while saving a 5.0 file
128                 SAL_INFO_IF( aLastUserName.isEmpty(), "sc.ui", "Author not found" );
129                 nLastUserIndex = 0;
130             }
131             else
132             {
133                 size_t nPos = std::distance(rUsers.begin(), it);
134                 nLastUserIndex = nPos % SC_AUTHORCOLORCOUNT;
135             }
136         }
137         nColor = nAuthorColor[nLastUserIndex];
138     }
139 }
140 
ScOutputData(OutputDevice * pNewDev,ScOutputType eNewType,ScTableInfo & rTabInfo,ScDocument * pNewDoc,SCTAB nNewTab,tools::Long nNewScrX,tools::Long nNewScrY,SCCOL nNewX1,SCROW nNewY1,SCCOL nNewX2,SCROW nNewY2,double nPixelPerTwipsX,double nPixelPerTwipsY,const Fraction * pZoomX,const Fraction * pZoomY)141 ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
142                             ScTableInfo& rTabInfo, ScDocument* pNewDoc,
143                             SCTAB nNewTab, tools::Long nNewScrX, tools::Long nNewScrY,
144                             SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
145                             double nPixelPerTwipsX, double nPixelPerTwipsY,
146                             const Fraction* pZoomX, const Fraction* pZoomY ) :
147     mpOriginalTargetDevice( pNewDev ),
148     mpDev( pNewDev ),
149     mpRefDevice( pNewDev ),      // default is output device
150     pFmtDevice( pNewDev ),      // default is output device
151     mrTabInfo( rTabInfo ),
152     mpRowInfo( rTabInfo.mpRowInfo.get() ),
153     mnArrCount( rTabInfo.mnArrCount ),
154     mpDoc( pNewDoc ),
155     mnTab( nNewTab ),
156     mnScrX( nNewScrX ),
157     mnScrY( nNewScrY ),
158     mnX1( nNewX1 ),
159     mnY1( nNewY1 ),
160     mnX2( nNewX2 ),
161     mnY2( nNewY2 ),
162     meType( eNewType ),
163     mnPPTX( nPixelPerTwipsX ),
164     mnPPTY( nPixelPerTwipsY ),
165     mpViewShell( nullptr ),
166     mpDrawView( nullptr ),
167     mbEditMode( false ),
168     mnEditCol( 0 ),
169     mnEditRow( 0 ),
170     mbMetaFile( false ),
171     mbPagebreakMode( false ),
172     mbSolidBackground( false ),
173     mbUseStyleColor( false ),
174     mbForceAutoColor( officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get() ),
175     mbSyntaxMode( false ),
176     maGridColor( COL_BLACK ),
177     mbShowNullValues( true ),
178     mbShowFormulas( false ),
179     mbShowSpellErrors( false ),
180     mbMarkClipped( false ), // sal_False for printer/metafile etc.
181     mbSnapPixel( false ),
182     mbAnyClipped( false ),
183     mbVertical(false),
184     mpTargetPaintWindow(nullptr), // #i74769# use SdrPaintWindow direct
185     mpSpellCheckCxt(nullptr)
186 {
187     if (pZoomX)
188         maZoomX = *pZoomX;
189     else
190         maZoomX = Fraction(1,1);
191     if (pZoomY)
192         maZoomY = *pZoomY;
193     else
194         maZoomY = Fraction(1,1);
195 
196     mnVisX1 = mnX1;
197     mnVisY1 = mnY1;
198     mnVisX2 = mnX2;
199     mnVisY2 = mnY2;
200     mpDoc->StripHidden( mnVisX1, mnVisY1, mnVisX2, mnVisY2, mnTab );
201 
202     mnScrW = 0;
203     for (SCCOL nX=mnVisX1; nX<=mnVisX2; nX++)
204         mnScrW += mpRowInfo[0].basicCellInfo(nX).nWidth;
205 
206     mnMirrorW = mnScrW;
207 
208     mnScrH = 0;
209     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
210         mnScrH += mpRowInfo[nArrY].nHeight;
211 
212     mbTabProtected = mpDoc->IsTabProtected( mnTab );
213     mbLayoutRTL = mpDoc->IsLayoutRTL( mnTab );
214 
215     // always needed, so call at the end of the constructor
216     SetCellRotations();
217     InitOutputEditEngine();
218 }
219 
~ScOutputData()220 ScOutputData::~ScOutputData()
221 {
222 }
223 
SetSpellCheckContext(const sc::SpellCheckContext * pCxt)224 void ScOutputData::SetSpellCheckContext( const sc::SpellCheckContext* pCxt )
225 {
226     mpSpellCheckCxt = pCxt;
227 }
228 
SetContentDevice(OutputDevice * pContentDev)229 void ScOutputData::SetContentDevice( OutputDevice* pContentDev )
230 {
231     // use pContentDev instead of pDev where used
232 
233     if ( mpRefDevice == mpDev )
234         mpRefDevice = pContentDev;
235     if ( pFmtDevice == mpDev )
236         pFmtDevice = pContentDev;
237     mpDev = pContentDev;
238 }
239 
SetMirrorWidth(tools::Long nNew)240 void ScOutputData::SetMirrorWidth( tools::Long nNew )
241 {
242     mnMirrorW = nNew;
243 }
244 
SetGridColor(const Color & rColor)245 void ScOutputData::SetGridColor( const Color& rColor )
246 {
247     maGridColor = rColor;
248 }
249 
SetMarkClipped(bool bSet)250 void ScOutputData::SetMarkClipped( bool bSet )
251 {
252     mbMarkClipped = bSet;
253 }
254 
SetShowNullValues(bool bSet)255 void ScOutputData::SetShowNullValues( bool bSet )
256 {
257     mbShowNullValues = bSet;
258 }
259 
SetShowFormulas(bool bSet)260 void ScOutputData::SetShowFormulas( bool bSet )
261 {
262     mbShowFormulas = bSet;
263 }
264 
SetShowSpellErrors(bool bSet)265 void ScOutputData::SetShowSpellErrors( bool bSet )
266 {
267     mbShowSpellErrors = bSet;
268     // reset EditEngine because it depends on bShowSpellErrors
269     mxOutputEditEngine.reset();
270 }
271 
SetSnapPixel()272 void ScOutputData::SetSnapPixel()
273 {
274     mbSnapPixel = true;
275 }
276 
SetEditCell(SCCOL nCol,SCROW nRow)277 void ScOutputData::SetEditCell( SCCOL nCol, SCROW nRow )
278 {
279     mnEditCol = nCol;
280     mnEditRow = nRow;
281     mbEditMode = true;
282 }
283 
SetMetaFileMode(bool bNewMode)284 void ScOutputData::SetMetaFileMode( bool bNewMode )
285 {
286     mbMetaFile = bNewMode;
287 }
288 
SetSyntaxMode(bool bNewMode)289 void ScOutputData::SetSyntaxMode( bool bNewMode )
290 {
291     mbSyntaxMode = bNewMode;
292     if ( bNewMode && !mxValueColor )
293     {
294         const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
295         mxValueColor = rColorCfg.GetColorValue( svtools::CALCVALUE ).nColor;
296         mxTextColor = rColorCfg.GetColorValue( svtools::CALCTEXT ).nColor;
297         mxFormulaColor = rColorCfg.GetColorValue( svtools::CALCFORMULA ).nColor;
298     }
299 }
300 
ReopenPDFStructureElement(vcl::pdf::StructElement aType,SCROW nRow,SCCOL nCol)301 bool ScOutputData::ReopenPDFStructureElement(vcl::pdf::StructElement aType, SCROW nRow, SCCOL nCol)
302 {
303     bool bReopenTag = false;
304     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
305     if (pPDF)
306     {
307         if (aType == vcl::pdf::StructElement::Part) // Worksheet
308         {
309             if (pPDF->GetScPDFState()->m_WorksheetId != -1)
310             {
311                 sal_Int32 nId = pPDF->GetScPDFState()->m_WorksheetId;
312                 pPDF->BeginStructureElement(nId);
313                 bReopenTag = true;
314             }
315         }
316         else if (aType == vcl::pdf::StructElement::TableRow)
317         {
318             const auto aIter = pPDF->GetScPDFState()->m_TableRowMap.find(nRow);
319             if (aIter != pPDF->GetScPDFState()->m_TableRowMap.end() && nRow == aIter->first)
320             {
321                 sal_Int32 nId = (*aIter).second;
322                 pPDF->BeginStructureElement(nId);
323                 bReopenTag = true;
324             }
325         }
326         else if (aType == vcl::pdf::StructElement::TableData)
327         {
328             const std::pair<SCROW, SCCOL> keyToFind = std::make_pair(nRow, nCol);
329             const auto aIter = pPDF->GetScPDFState()->m_TableDataMap.find(keyToFind);
330             if (aIter != pPDF->GetScPDFState()->m_TableDataMap.end() && keyToFind == aIter->first)
331             {
332                 sal_Int32 nId = (*aIter).second;
333                 pPDF->BeginStructureElement(nId);
334                 bReopenTag = true;
335             }
336         }
337     }
338 
339     return bReopenTag;
340 }
341 
DrawGrid(vcl::RenderContext & rRenderContext,bool bGrid,bool bPage,bool bMergeCover)342 void ScOutputData::DrawGrid(vcl::RenderContext& rRenderContext, bool bGrid, bool bPage, bool bMergeCover)
343 {
344     // bMergeCover : Draw lines in sheet bgcolor to cover lok client grid lines in merged cell areas.
345     // When scNoGridBackground is set in lok mode, bMergeCover is set to true and bGrid to false.
346 
347     SCCOL nX;
348     SCROW nY;
349     tools::Long nPosX;
350     tools::Long nPosY;
351     SCSIZE nArrY;
352     ScBreakType nBreak    = ScBreakType::NONE;
353     ScBreakType nBreakOld = ScBreakType::NONE;
354 
355     bool bDashed = false;
356     Color aPageColor;
357     Color aManualColor;
358 
359     if (mbPagebreakMode)
360         bPage = false;          // no "normal" breaks over the whole width/height
361 
362     // It is a big mess to distinguish when we are using pixels and when logic
363     // units for drawing.  Ultimately we want to work only in the logic units,
364     // but until that happens, we need to special-case:
365     //
366     //   * metafile
367     //   * drawing to the screen - everything is internally counted in pixels there
368     //
369     // 'Internally' in the above means the pCellInfo[...].nWidth and
370     // mpRowInfo[...]->nHeight:
371     //
372     //   * when bWorksInPixels is true: these are in pixels
373     //   * when bWorksInPixels is false: these are in the logic units
374     //
375     // This is where all the confusion comes from, ultimately we want them
376     // always in the logic units (100th of millimeters), but we need to get
377     // there gradually (get rid of setting MapUnit::MapPixel first), otherwise we'd
378     // break all the drawing by one change.
379     // So until that happens, we need to special case.
380     bool bWorksInPixels = mbMetaFile;
381     const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
382     Color aSheetBGColor = rColorCfg.GetColorValue(::svtools::DOCCOLOR).nColor;
383 
384     if ( meType == OUTTYPE_WINDOW )
385     {
386         bWorksInPixels = true;
387         aPageColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
388         aManualColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor;
389     }
390     else
391     {
392         aPageColor = maGridColor;
393         aManualColor = maGridColor;
394     }
395 
396     tools::Long nOneX = 1;
397     tools::Long nOneY = 1;
398     if (!bWorksInPixels)
399     {
400         Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
401         nOneX = aOnePixel.Width();
402         nOneY = aOnePixel.Height();
403     }
404 
405     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
406     tools::Long nSignedOneX = nOneX * nLayoutSign;
407 
408     rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : maGridColor);
409 
410     ScGridMerger aGrid(&rRenderContext, nOneX, nOneY);
411 
412     // vertical lines
413 
414     nPosX = mnScrX;
415     if ( mbLayoutRTL )
416         nPosX += mnMirrorW - nOneX;
417 
418     for (nX=mnX1; nX<=mnX2; nX++)
419     {
420         sal_uInt16 nWidth = mpRowInfo[0].basicCellInfo(nX).nWidth;
421         if (nWidth)
422         {
423             nPosX += nWidth * nLayoutSign;
424 
425             if ( bPage )
426             {
427                 // Search also in hidden part for page breaks
428                 SCCOL nCol = nX + 1;
429                 while (nCol <= mpDoc->MaxCol())
430                 {
431                     nBreak = mpDoc->HasColBreak(nCol, mnTab);
432                     bool bHidden = mpDoc->ColHidden(nCol, mnTab);
433 
434                     if ( nBreak != ScBreakType::NONE || !bHidden )
435                         break;
436                     ++nCol;
437                 }
438 
439                 if (nBreak != nBreakOld)
440                 {
441                     aGrid.Flush();
442 
443                     if (static_cast<int>(nBreak))
444                     {
445                         rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
446                                                         aPageColor );
447                         bDashed = true;
448                     }
449                     else
450                     {
451                         rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : maGridColor);
452                         bDashed = false;
453                     }
454 
455                     nBreakOld = nBreak;
456                 }
457             }
458 
459             bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set that way
460 
461             sal_uInt16 nWidthXplus1 = mpRowInfo[0].basicCellInfo(nX+1).nWidth;
462             bool bSingle = false; //! get into Fillinfo !!!!!
463             if ( nX<mpDoc->MaxCol() )
464             {
465                 bSingle = ( nWidthXplus1 == 0 );
466                 for (nArrY=1; nArrY+1<mnArrCount && !bSingle; nArrY++)
467                 {
468                     if (mpRowInfo[nArrY].cellInfo(nX+1).bHOverlapped)
469                         bSingle = true;
470                     if (mpRowInfo[nArrY].cellInfo(nX).bHideGrid)
471                         bSingle = true;
472                 }
473             }
474 
475             if (bDraw)
476             {
477                 if ( nX<mpDoc->MaxCol() && bSingle )
478                 {
479                     SCCOL nVisX = nX + 1;
480                     while ( nVisX < mpDoc->MaxCol() && !mpDoc->GetColWidth(nVisX,mnTab) )
481                         ++nVisX;
482 
483                     nPosY = mnScrY;
484                     for (nArrY=1; nArrY+1<mnArrCount; nArrY++)
485                     {
486                         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
487                         const tools::Long nNextY = nPosY + pThisRowInfo->nHeight;
488 
489                         bool bHOver = pThisRowInfo->cellInfo(nX).bHideGrid;
490                         if (!bHOver)
491                         {
492                             if (nWidthXplus1)
493                                 bHOver = pThisRowInfo->cellInfo(nX+1).bHOverlapped;
494                             else
495                             {
496                                 if (nVisX <= mnX2)
497                                     bHOver = pThisRowInfo->cellInfo(nVisX).bHOverlapped;
498                                 else
499                                     bHOver = mpDoc->GetAttr(
500                                                 nVisX,pThisRowInfo->nRowNo,mnTab,ATTR_MERGE_FLAG)
501                                                 ->IsHorOverlapped();
502                                 if (bHOver)
503                                     bHOver = mpDoc->GetAttr(
504                                                 nX + 1,pThisRowInfo->nRowNo,mnTab,ATTR_MERGE_FLAG)
505                                                 ->IsHorOverlapped();
506                             }
507                         }
508 
509                         if ((pThisRowInfo->bChanged && !bHOver && !bMergeCover) || (bHOver && bMergeCover))
510                         {
511                             aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nPosY, nNextY-nOneY, bDashed);
512                         }
513                         nPosY = nNextY;
514                     }
515                 }
516                 else if (!bMergeCover)
517                 {
518                     aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, mnScrY, mnScrY+mnScrH-nOneY, bDashed);
519                 }
520             }
521         }
522     }
523 
524     // horizontal lines
525 
526     bool bHiddenRow = true;
527     SCROW nHiddenEndRow = -1;
528     nPosY = mnScrY;
529     for (nArrY=1; nArrY+1<mnArrCount; nArrY++)
530     {
531         SCSIZE nArrYplus1 = nArrY+1;
532         nY = mpRowInfo[nArrY].nRowNo;
533         SCROW nYplus1 = nY+1;
534         nPosY += mpRowInfo[nArrY].nHeight;
535 
536         if (mpRowInfo[nArrY].bChanged)
537         {
538             if ( bPage )
539             {
540                 for (SCROW i = nYplus1; i <= mpDoc->MaxRow(); ++i)
541                 {
542                     if (i > nHiddenEndRow)
543                         bHiddenRow = mpDoc->RowHidden(i, mnTab, nullptr, &nHiddenEndRow);
544                     /* TODO: optimize the row break thing for large hidden
545                      * segments where HasRowBreak() has to be called
546                      * nevertheless for each row, as a row break is drawn also
547                      * for hidden rows, above them. This needed to be done only
548                      * once per hidden segment, maybe giving manual breaks
549                      * priority. Something like GetNextRowBreak() and
550                      * GetNextManualRowBreak(). */
551                     nBreak = mpDoc->HasRowBreak(i, mnTab);
552                     if (!bHiddenRow || nBreak != ScBreakType::NONE)
553                         break;
554                 }
555 
556                 if (nBreakOld != nBreak)
557                 {
558                     aGrid.Flush();
559 
560                     if (static_cast<int>(nBreak))
561                     {
562                         rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
563                                                         aPageColor );
564                         bDashed = true;
565                     }
566                     else
567                     {
568                         rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : maGridColor);
569                         bDashed = false;
570                     }
571 
572                     nBreakOld = nBreak;
573                 }
574             }
575 
576             bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover;    // simple grid only if set so
577 
578             bool bNextYisNextRow = (mpRowInfo[nArrYplus1].nRowNo == nYplus1);
579             bool bSingle = !bNextYisNextRow;             // Hidden
580             for (SCCOL i=mnX1; i<=mnX2 && !bSingle; i++)
581             {
582                 if (mpRowInfo[nArrYplus1].cellInfo(i).bVOverlapped)
583                     bSingle = true;
584             }
585 
586             if (bDraw)
587             {
588                 if ( bSingle && nY<mpDoc->MaxRow() )
589                 {
590                     SCROW nVisY = mpRowInfo[nArrYplus1].nRowNo;
591 
592                     nPosX = mnScrX;
593                     if ( mbLayoutRTL )
594                         nPosX += mnMirrorW - nOneX;
595 
596                     for (SCCOL i=mnX1; i<=mnX2; i++)
597                     {
598                         const tools::Long nNextX = nPosX + mpRowInfo[0].basicCellInfo(i).nWidth * nLayoutSign;
599                         if (nNextX != nPosX)                                // visible
600                         {
601                             bool bVOver;
602                             if ( bNextYisNextRow )
603                                 bVOver = mpRowInfo[nArrYplus1].cellInfo(i).bVOverlapped;
604                             else
605                             {
606                                 bVOver = mpDoc->GetAttr(
607                                             i,nYplus1,mnTab,ATTR_MERGE_FLAG)
608                                             ->IsVerOverlapped()
609                                     &&   mpDoc->GetAttr(
610                                             i,nVisY,mnTab,ATTR_MERGE_FLAG)
611                                             ->IsVerOverlapped();
612                                     //! nVisY from Array ??
613                             }
614 
615                             if ((!bVOver && !bMergeCover) || (bVOver && bMergeCover))
616                             {
617                                 aGrid.AddHorLine(bWorksInPixels, nPosX, nNextX-nSignedOneX, nPosY-nOneY, bDashed);
618                             }
619                         }
620                         nPosX = nNextX;
621                     }
622                 }
623                 else if (!bMergeCover)
624                 {
625                     aGrid.AddHorLine(bWorksInPixels, mnScrX, mnScrX+mnScrW-nOneX, nPosY-nOneY, bDashed);
626                 }
627             }
628         }
629     }
630 }
631 
SetPagebreakMode(ScPageBreakData * pPageData)632 void ScOutputData::SetPagebreakMode( ScPageBreakData* pPageData )
633 {
634     mbPagebreakMode = true;
635     if (!pPageData)
636         return;                     // not yet initialized -> everything "not printed"
637 
638     // mark printed range
639     // (everything in FillInfo is already initialized to sal_False)
640 
641     sal_uInt16 nRangeCount = sal::static_int_cast<sal_uInt16>(pPageData->GetCount());
642     for (sal_uInt16 nPos=0; nPos<nRangeCount; nPos++)
643     {
644         ScRange aRange = pPageData->GetData( nPos ).GetPrintRange();
645 
646         SCCOL nStartX = std::max( aRange.aStart.Col(), mnX1 );
647         SCCOL nEndX   = std::min( aRange.aEnd.Col(),   mnX2 );
648         SCROW nStartY = std::max( aRange.aStart.Row(), mnY1 );
649         SCROW nEndY   = std::min( aRange.aEnd.Row(),   mnY2 );
650 
651         for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
652         {
653             RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
654             if ( pThisRowInfo->bChanged && pThisRowInfo->nRowNo >= nStartY &&
655                                            pThisRowInfo->nRowNo <= nEndY )
656             {
657                 for (SCCOL nX=nStartX; nX<=nEndX; nX++)
658                     pThisRowInfo->cellInfo(nX).bPrinted = true;
659             }
660         }
661     }
662 }
663 
SetCellRotations()664 void ScOutputData::SetCellRotations()
665 {
666     //! save nRotMax
667     SCCOL nRotMax = mnX2;
668     for (SCSIZE nRotY=0; nRotY<mnArrCount; nRotY++)
669         if (mpRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && mpRowInfo[nRotY].nRotMaxCol > nRotMax)
670             nRotMax = mpRowInfo[nRotY].nRotMaxCol;
671 
672     for (SCSIZE nArrY=1; nArrY<mnArrCount; nArrY++)
673     {
674         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
675         if ( pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE &&
676              ( pThisRowInfo->bChanged || mpRowInfo[nArrY-1].bChanged ||
677                ( nArrY+1<mnArrCount && mpRowInfo[nArrY+1].bChanged ) ) )
678         {
679             SCROW nY = pThisRowInfo->nRowNo;
680 
681             for (SCCOL nX=0; nX<=nRotMax; nX++)
682             {
683                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
684                 const ScPatternAttr* pPattern = pInfo->pPatternAttr;
685                 const SfxItemSet* pCondSet = pInfo->pConditionSet;
686 
687                 if ( !pPattern && !mpDoc->ColHidden(nX, mnTab) )
688                 {
689                     pPattern = mpDoc->GetPattern( nX, nY, mnTab );
690                     pCondSet = mpDoc->GetCondResult( nX, nY, mnTab );
691                 }
692 
693                 if ( pPattern )     // column isn't hidden
694                 {
695                     ScRotateDir nDir = pPattern->GetRotateDir( pCondSet );
696                     if (nDir != ScRotateDir::NONE)
697                     {
698                         // Needed for ScCellInfo internal decisions (bg fill, ...)
699                         pInfo->nRotateDir = nDir;
700 
701                         // create target coordinates
702                         const SCCOL nTargetX(nX - mnVisX1 + 1);
703                         const SCROW nTargetY(nY - mnVisY1 + 1);
704 
705                         // Check for values - below in SetCellRotation these will
706                         // be converted to size_t and thus may not be negative
707                         // tdf#168023: also do not rotate if the cell is empty
708                         if(nTargetX >= 0 && nTargetY >= 0 && !pInfo->maCell.hasEmptyValue())
709                         {
710                             // add rotation info to Array information
711                             const Degree100 nAttrRotate(pPattern->GetRotateVal(pCondSet));
712                             const SvxRotateMode eRotMode(pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue());
713                             const double fOrient((mbLayoutRTL ? -1.0 : 1.0) * toRadians(nAttrRotate)); // 1/100th degrees -> [0..2PI]
714                             svx::frame::Array& rArray = mrTabInfo.maArray;
715 
716                             rArray.SetCellRotation(nTargetX, nTargetY, eRotMode, fOrient);
717                         }
718                     }
719                 }
720             }
721         }
722     }
723 }
724 
lcl_GetRotateDir(const ScDocument * pDoc,SCCOL nCol,SCROW nRow,SCTAB mnTab)725 static ScRotateDir lcl_GetRotateDir( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB mnTab )
726 {
727     const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, mnTab );
728     const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, mnTab );
729 
730     ScRotateDir nRet = ScRotateDir::NONE;
731 
732     Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
733     if ( nAttrRotate )
734     {
735         SvxRotateMode eRotMode =
736                     pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
737 
738         if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
739             nRet = ScRotateDir::Standard;
740         else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
741             nRet = ScRotateDir::Center;
742         else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
743         {
744             tools::Long nRot180 = nAttrRotate.get() % 18000;     // 1/100 degree
745             if ( nRot180 == 9000 )
746                 nRet = ScRotateDir::Center;
747             else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) ||
748                       ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) )
749                 nRet = ScRotateDir::Left;
750             else
751                 nRet = ScRotateDir::Right;
752         }
753     }
754 
755     return nRet;
756 }
757 
lcl_FindBackground(const ScDocument * pDoc,SCCOL nCol,SCROW nRow,SCTAB mnTab)758 static const SvxBrushItem* lcl_FindBackground( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB mnTab )
759 {
760     const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, mnTab );
761     const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, mnTab );
762     const SvxBrushItem* pBackground =
763                             &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
764 
765     ScRotateDir nDir = lcl_GetRotateDir( pDoc, nCol, nRow, mnTab );
766 
767     // treat CENTER like RIGHT
768     if ( nDir == ScRotateDir::Right || nDir == ScRotateDir::Center )
769     {
770         // text goes to the right -> take background from the left
771         while ( nCol > 0 && lcl_GetRotateDir( pDoc, nCol, nRow, mnTab ) == nDir &&
772                             pBackground->GetColor().GetAlpha() != 0 )
773         {
774             --nCol;
775             pPattern = pDoc->GetPattern( nCol, nRow, mnTab );
776             pCondSet = pDoc->GetCondResult( nCol, nRow, mnTab );
777             pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
778         }
779     }
780     else if ( nDir == ScRotateDir::Left )
781     {
782         // text goes to the left -> take background from the right
783         while ( nCol < pDoc->MaxCol() && lcl_GetRotateDir( pDoc, nCol, nRow, mnTab ) == nDir &&
784                             pBackground->GetColor().GetAlpha() != 0 )
785         {
786             ++nCol;
787             pPattern = pDoc->GetPattern( nCol, nRow, mnTab );
788             pCondSet = pDoc->GetCondResult( nCol, nRow, mnTab );
789             pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
790         }
791     }
792 
793     return pBackground;
794 }
795 
lcl_EqualBack(const RowInfo & rFirst,const RowInfo & rOther,SCCOL mnX1,SCCOL mnX2,bool bShowProt,bool bPagebreakMode)796 static bool lcl_EqualBack( const RowInfo& rFirst, const RowInfo& rOther,
797                     SCCOL mnX1, SCCOL mnX2, bool bShowProt, bool bPagebreakMode )
798 {
799     if ( rFirst.bChanged   != rOther.bChanged ||
800          rFirst.bEmptyBack != rOther.bEmptyBack )
801         return false;
802 
803     SCCOL nX;
804     if ( bShowProt )
805     {
806         for ( nX=mnX1; nX<=mnX2; nX++ )
807         {
808             const ScPatternAttr* pPat1 = rFirst.cellInfo(nX).pPatternAttr;
809             const ScPatternAttr* pPat2 = rOther.cellInfo(nX).pPatternAttr;
810             if ( !pPat1 || !pPat2 ||
811                     !SfxPoolItem::areSame(pPat1->GetItem(ATTR_PROTECTION), pPat2->GetItem(ATTR_PROTECTION) ) )
812                 return false;
813         }
814     }
815     else
816     {
817         for ( nX=mnX1; nX<=mnX2; nX++ )
818             if ( !SfxPoolItem::areSame(rFirst.cellInfo(nX).maBackground.getItem(), rOther.cellInfo(nX).maBackground.getItem() ) )
819                 return false;
820     }
821 
822     if ( rFirst.nRotMaxCol != SC_ROTMAX_NONE || rOther.nRotMaxCol != SC_ROTMAX_NONE )
823         for ( nX=mnX1; nX<=mnX2; nX++ )
824             if ( rFirst.cellInfo(nX).nRotateDir != rOther.cellInfo(nX).nRotateDir )
825                 return false;
826 
827     if ( bPagebreakMode )
828         for ( nX=mnX1; nX<=mnX2; nX++ )
829             if ( rFirst.cellInfo(nX).bPrinted != rOther.cellInfo(nX).bPrinted )
830                 return false;
831 
832     for ( nX=mnX1; nX<=mnX2; nX++ )
833     {
834         std::optional<Color> const & pCol1 = rFirst.cellInfo(nX).mxColorScale;
835         std::optional<Color> const & pCol2 = rOther.cellInfo(nX).mxColorScale;
836         if( (pCol1 && !pCol2) || (!pCol1 && pCol2) )
837             return false;
838 
839         if (pCol1 && (*pCol1 != *pCol2))
840             return false;
841 
842         const ScDataBarInfo* pInfo1 = rFirst.cellInfo(nX).pDataBar;
843         const ScDataBarInfo* pInfo2 = rOther.cellInfo(nX).pDataBar;
844 
845         if( (pInfo1 && !pInfo2) || (!pInfo1 && pInfo2) )
846             return false;
847 
848         if (pInfo1 && (*pInfo1 != *pInfo2))
849             return false;
850 
851         // each cell with an icon set should be painted the same way
852         const ScIconSetInfo* pIconSet1 = rFirst.cellInfo(nX).pIconSet;
853         const ScIconSetInfo* pIconSet2 = rOther.cellInfo(nX).pIconSet;
854 
855         if(pIconSet1 || pIconSet2)
856             return false;
857     }
858 
859     return true;
860 }
861 
DrawDocumentBackground()862 void ScOutputData::DrawDocumentBackground()
863 {
864     if ( !mbSolidBackground )
865         return;
866 
867     Color aBgColor(ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
868     mpDev->SetLineColor(aBgColor);
869     mpDev->SetFillColor(aBgColor);
870 
871     Point aScreenPos  = mpDev->PixelToLogic(Point(mnScrX, mnScrY));
872     Size  aScreenSize = mpDev->PixelToLogic(Size(mnScrW - 1,mnScrH - 1));
873 
874     mpDev->DrawRect(tools::Rectangle(aScreenPos, aScreenSize));
875 }
876 
877 namespace {
878 
879 const double lclCornerRectTransparency = 40.0;
880 
drawDataBars(vcl::RenderContext & rRenderContext,const ScDataBarInfo * pOldDataBarInfo,const tools::Rectangle & rRect,tools::Long nOneX,tools::Long nOneY)881 void drawDataBars(vcl::RenderContext& rRenderContext, const ScDataBarInfo* pOldDataBarInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY)
882 {
883     tools::Long nPosZero = 0;
884     tools::Rectangle aPaintRect = rRect;
885     aPaintRect.AdjustTop(2 * nOneY );
886     aPaintRect.AdjustBottom( -(2 * nOneY) );
887     aPaintRect.AdjustLeft( 2 * nOneX );
888     aPaintRect.AdjustRight( -(2 * nOneX) );
889     if(pOldDataBarInfo->mnZero)
890     {
891         // need to calculate null point in cell
892         tools::Long nLength = aPaintRect.Right() - aPaintRect.Left();
893         nPosZero = static_cast<tools::Long>(aPaintRect.Left() + nLength*pOldDataBarInfo->mnZero/100.0);
894     }
895     else
896     {
897         nPosZero = aPaintRect.Left();
898     }
899 
900     if(pOldDataBarInfo->mnLength < 0)
901     {
902         aPaintRect.SetRight( nPosZero );
903         tools::Long nLength = nPosZero - aPaintRect.Left();
904         aPaintRect.SetLeft( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
905     }
906     else if(pOldDataBarInfo->mnLength > 0)
907     {
908         aPaintRect.SetLeft( nPosZero );
909         tools::Long nLength = aPaintRect.Right() - nPosZero;
910         aPaintRect.SetRight( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
911     }
912     else
913         return;
914 
915     if(pOldDataBarInfo->mbGradient)
916     {
917         rRenderContext.SetLineColor(pOldDataBarInfo->maColor);
918         Gradient aGradient(css::awt::GradientStyle_LINEAR, pOldDataBarInfo->maColor, COL_TRANSPARENT);
919         aGradient.SetSteps(255);
920 
921         if(pOldDataBarInfo->mnLength < 0)
922             aGradient.SetAngle(2700_deg10);
923         else
924             aGradient.SetAngle(900_deg10);
925 
926         rRenderContext.DrawGradient(aPaintRect, aGradient);
927 
928         rRenderContext.SetLineColor();
929     }
930     else
931     {
932         rRenderContext.SetFillColor(pOldDataBarInfo->maColor);
933         rRenderContext.DrawRect(aPaintRect);
934     }
935 
936     //draw axis
937     if(!(pOldDataBarInfo->mnZero && pOldDataBarInfo->mnZero != 100))
938         return;
939 
940     Point aPoint1(nPosZero, rRect.Top());
941     Point aPoint2(nPosZero, rRect.Bottom());
942     LineInfo aLineInfo(LineStyle::Dash, 1);
943     aLineInfo.SetDashCount( 4 );
944     aLineInfo.SetDistance( 3 );
945     aLineInfo.SetDashLen( 3 );
946     rRenderContext.SetFillColor(pOldDataBarInfo->maAxisColor);
947     rRenderContext.SetLineColor(pOldDataBarInfo->maAxisColor);
948     rRenderContext.DrawLine(aPoint1, aPoint2, aLineInfo);
949     rRenderContext.SetLineColor();
950     rRenderContext.SetFillColor();
951 }
952 
getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap,ScIconSetType eType,sal_Int32 nIndex)953 const Bitmap& getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap, ScIconSetType eType, sal_Int32 nIndex)
954 {
955     return ScIconSetFormat::getBitmap(rIconSetBitmapMap, eType, nIndex);
956 }
957 
drawIconSets(vcl::RenderContext & rRenderContext,const ScIconSetInfo * pOldIconSetInfo,const tools::Rectangle & rRect,tools::Long nOneX,tools::Long nOneY,sc::IconSetBitmapMap & rIconSetBitmapMap)958 void drawIconSets(vcl::RenderContext& rRenderContext, const ScIconSetInfo* pOldIconSetInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY,
959         sc::IconSetBitmapMap & rIconSetBitmapMap)
960 {
961     ScIconSetType eType = pOldIconSetInfo->eIconSetType;
962     sal_Int32 nIndex = pOldIconSetInfo->nIconIndex;
963     const Bitmap& rIcon = getIcon(rIconSetBitmapMap, eType, nIndex);
964 
965     tools::Long aHeight = o3tl::convert(10, o3tl::Length::pt, o3tl::Length::mm100);
966 
967     if (pOldIconSetInfo->mnHeight)
968     {
969         if (comphelper::LibreOfficeKit::isActive())
970         {
971             aHeight = rRenderContext.LogicToPixel(Size(0, pOldIconSetInfo->mnHeight), MapMode(MapUnit::MapTwip)).Height();
972             aHeight *= comphelper::LibreOfficeKit::getDPIScale();
973         }
974         else
975         {
976             aHeight = o3tl::convert(pOldIconSetInfo->mnHeight, o3tl::Length::twip, o3tl::Length::mm100);
977         }
978     }
979 
980     Size aSize = rIcon.GetSizePixel();
981     double fRatio = static_cast<double>(aSize.Width()) / aSize.Height();
982     tools::Long aWidth = fRatio * aHeight;
983 
984     auto popIt = rRenderContext.ScopedPush();
985     rRenderContext.SetClipRegion(vcl::Region(rRect));
986     rRenderContext.DrawBitmapEx(Point(rRect.Left() + 2 * nOneX, rRect.Bottom() - 2 * nOneY - aHeight), Size(aWidth, aHeight), rIcon);
987 }
988 
drawCells(vcl::RenderContext & rRenderContext,std::optional<Color> const & pColor,const SvxBrushItem * pBackground,std::optional<Color> & pOldColor,const SvxBrushItem * & pOldBackground,tools::Rectangle & rRect,tools::Long nPosX,tools::Long nLayoutSign,tools::Long nOneX,tools::Long nOneY,const ScDataBarInfo * pDataBarInfo,const ScDataBarInfo * & pOldDataBarInfo,const ScIconSetInfo * pIconSetInfo,const ScIconSetInfo * & pOldIconSetInfo,sc::IconSetBitmapMap & rIconSetBitmapMap)989 void drawCells(vcl::RenderContext& rRenderContext, std::optional<Color> const & pColor, const SvxBrushItem* pBackground, std::optional<Color>& pOldColor, const SvxBrushItem*& pOldBackground,
990         tools::Rectangle& rRect, tools::Long nPosX, tools::Long nLayoutSign, tools::Long nOneX, tools::Long nOneY, const ScDataBarInfo* pDataBarInfo, const ScDataBarInfo*& pOldDataBarInfo,
991         const ScIconSetInfo* pIconSetInfo, const ScIconSetInfo*& pOldIconSetInfo,
992         sc::IconSetBitmapMap & rIconSetBitmapMap)
993 {
994     tools::Long nSignedOneX = nOneX * nLayoutSign;
995     // need to paint if old color scale has been used and now
996     // we have a different color or a style based background
997     // we can here fall back to pointer comparison
998     if (pOldColor && (pBackground || pOldColor != pColor || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo))
999     {
1000         rRect.SetRight( nPosX-nSignedOneX );
1001         if( !pOldColor->IsTransparent() )
1002         {
1003             rRenderContext.SetFillColor( *pOldColor );
1004             rRenderContext.DrawRect( rRect );
1005         }
1006         if( pOldDataBarInfo )
1007             drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
1008         if( pOldIconSetInfo )
1009             drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
1010 
1011         rRect.SetLeft( nPosX - nSignedOneX );
1012     }
1013 
1014     if ( pOldBackground && (pColor || !SfxPoolItem::areSame(pBackground, pOldBackground) || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo) )
1015     {
1016         rRect.SetRight( nPosX-nSignedOneX );
1017         if (pOldBackground)             // ==0 if hidden
1018         {
1019             Color aBackCol = pOldBackground->GetColor();
1020             if ( !aBackCol.IsTransparent() )      //! partial transparency?
1021             {
1022                 rRenderContext.SetFillColor( aBackCol );
1023                 rRenderContext.DrawRect( rRect );
1024             }
1025         }
1026         if( pOldDataBarInfo )
1027             drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
1028         if( pOldIconSetInfo )
1029             drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
1030 
1031         rRect.SetLeft( nPosX - nSignedOneX );
1032     }
1033 
1034     if (!pOldBackground && !pOldColor && (pDataBarInfo || pIconSetInfo))
1035     {
1036         rRect.SetRight( nPosX -nSignedOneX );
1037         rRect.SetLeft( nPosX - nSignedOneX );
1038     }
1039 
1040     if(pColor)
1041     {
1042         // only update pOldColor if the colors changed
1043         if (!pOldColor || *pOldColor != *pColor)
1044             pOldColor = pColor;
1045 
1046         pOldBackground = nullptr;
1047     }
1048     else if(pBackground)
1049     {
1050         pOldBackground = pBackground;
1051         pOldColor.reset();
1052     }
1053 
1054     if(pDataBarInfo)
1055         pOldDataBarInfo = pDataBarInfo;
1056     else
1057         pOldDataBarInfo = nullptr;
1058 
1059     if(pIconSetInfo)
1060         pOldIconSetInfo = pIconSetInfo;
1061     else
1062         pOldIconSetInfo = nullptr;
1063 }
1064 
1065 }
1066 
DrawBackground(vcl::RenderContext & rRenderContext)1067 void ScOutputData::DrawBackground(vcl::RenderContext& rRenderContext)
1068 {
1069     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1070     bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1071 
1072     Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
1073     tools::Long nOneXLogic = aOnePixel.Width();
1074     tools::Long nOneYLogic = aOnePixel.Height();
1075 
1076     // See more about bWorksInPixels in ScOutputData::DrawGrid
1077     bool bWorksInPixels = (meType == OUTTYPE_WINDOW);
1078     const tools::Long nOneX = bWorksInPixels ? 1 : nOneXLogic;
1079     const tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
1080     const tools::Long nSignedOneX = nOneX * nLayoutSign;
1081 
1082     tools::Rectangle aRect;
1083 
1084     rRenderContext.SetLineColor();
1085 
1086     bool bShowProt = mbSyntaxMode && mpDoc->IsTabProtected(mnTab);
1087     bool bDoAll = bShowProt || mbPagebreakMode || mbSolidBackground;
1088 
1089     bool bCellContrast = mbUseStyleColor &&
1090             Application::GetSettings().GetStyleSettings().GetHighContrastMode();
1091 
1092     tools::Long nPosY = mnScrY;
1093 
1094     const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
1095     Color aProtectedColor( rColorCfg.GetColorValue( svtools::CALCPROTECTEDBACKGROUND ).nColor );
1096     auto pProtectedBackground = std::make_shared<SvxBrushItem>( aProtectedColor, ATTR_BACKGROUND );
1097 
1098     // iterate through the rows to show
1099     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
1100     {
1101         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1102         tools::Long nRowHeight = pThisRowInfo->nHeight;
1103 
1104         if ( pThisRowInfo->bChanged )
1105         {
1106             if ( ( ( pThisRowInfo->bEmptyBack ) || mbSyntaxMode ) && !bDoAll )
1107             {
1108                 // nothing
1109             }
1110             else
1111             {
1112                 if (bTaggedPDF)
1113                     pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::NonStructElement);
1114 
1115                 // scan for rows with the same background:
1116                 SCSIZE nSkip = 0;
1117                 while ( nArrY+nSkip+2<mnArrCount &&
1118                         lcl_EqualBack( *pThisRowInfo, mpRowInfo[nArrY+nSkip+1],
1119                                         mnX1, mnX2, bShowProt, mbPagebreakMode ) )
1120                 {
1121                     ++nSkip;
1122                     nRowHeight += mpRowInfo[nArrY+nSkip].nHeight;    // after incrementing
1123                 }
1124 
1125                 tools::Long nPosX = mnScrX;
1126                 if ( mbLayoutRTL )
1127                     nPosX += mnMirrorW - nOneX;
1128 
1129                 // tdf#135891 - adjust the x position to ensure the correct starting point
1130                 if (!bWorksInPixels)
1131                     nPosX -= nLayoutSign + 1;
1132 
1133                 aRect = tools::Rectangle(nPosX, nPosY - 1, nPosX, nPosY - 1 + nRowHeight);
1134                 if (bWorksInPixels)
1135                     aRect = rRenderContext.PixelToLogic(aRect); // internal data in pixels, but we'll be drawing in logic units
1136 
1137                 const SvxBrushItem* pOldBackground = nullptr;
1138                 const SvxBrushItem* pBackground = nullptr;
1139                 std::optional<Color> pOldColor;
1140                 const ScDataBarInfo* pOldDataBarInfo = nullptr;
1141                 const ScIconSetInfo* pOldIconSetInfo = nullptr;
1142                 SCCOL nMergedCols = 1;
1143                 SCCOL nOldMerged = 0;
1144 
1145                 for (SCCOL nX=mnX1; nX + nMergedCols <= mnX2 + 1; nX += nOldMerged)
1146                 {
1147                     ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX-1+nMergedCols);
1148 
1149                     nOldMerged = nMergedCols;
1150 
1151                     tools::Long nNewPosX = nPosX;
1152                     // extend for all merged cells
1153                     nMergedCols = 1;
1154                     if (pInfo->bMerged && pInfo->pPatternAttr)
1155                     {
1156                             const ScMergeAttr* pMerge =
1157                                     &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
1158                             nMergedCols = std::max<SCCOL>(1, pMerge->GetColMerge());
1159                     }
1160 
1161                     for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
1162                     {
1163                         SCCOL nCol = nX+nOldMerged+nMerged;
1164                         if (nCol > mnX2+2)
1165                             break;
1166                         nNewPosX += mpRowInfo[0].basicCellInfo(nCol-1).nWidth * nLayoutSign;
1167                     }
1168 
1169                     if (nNewPosX == nPosX)
1170                         continue; // Zero width, no need to draw.
1171 
1172                     if (bCellContrast)
1173                     {
1174                         //  high contrast for cell borders and backgrounds -> empty background
1175                         pBackground = ScGlobal::GetEmptyBrushItem();
1176                     }
1177                     else if (bShowProt)         // show cell protection in syntax mode
1178                     {
1179                         const ScPatternAttr* pP = pInfo->pPatternAttr;
1180                         if (pP)
1181                         {
1182                             const ScProtectionAttr& rProt = pP->GetItem(ATTR_PROTECTION);
1183                             if (rProt.GetProtection() || rProt.GetHideCell())
1184                                 pBackground = pProtectedBackground.get();
1185                             else
1186                                 pBackground = ScGlobal::GetEmptyBrushItem();
1187                         }
1188                         else
1189                             pBackground = nullptr;
1190                     }
1191                     else
1192                         pBackground = static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem());
1193 
1194                     if ( mbPagebreakMode && !pInfo->bPrinted )
1195                         pBackground = pProtectedBackground.get();
1196 
1197                     if ( pInfo->nRotateDir > ScRotateDir::Standard &&
1198                             !pBackground->GetColor().IsFullyTransparent() &&
1199                             !bCellContrast )
1200                     {
1201                         SCROW nY = mpRowInfo[nArrY].nRowNo;
1202                         pBackground = lcl_FindBackground( mpDoc, nX, nY, mnTab );
1203                     }
1204 
1205                     std::optional<Color> const & pColor = pInfo->mxColorScale;
1206                     const ScDataBarInfo* pDataBarInfo = pInfo->pDataBar;
1207                     const ScIconSetInfo* pIconSetInfo = pInfo->pIconSet;
1208 
1209                     tools::Long nPosXLogic = nPosX;
1210                     if (bWorksInPixels)
1211                         nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
1212 
1213                     drawCells(rRenderContext, pColor, pBackground, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, pDataBarInfo, pOldDataBarInfo, pIconSetInfo, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
1214 
1215                     nPosX = nNewPosX;
1216                     // tdf#135891 - adjust the x position to ensure the correct starting point
1217                     if (!bWorksInPixels && nX == mnX1)
1218                         nPosX += nSignedOneX + 1;
1219                 }
1220 
1221                 tools::Long nPosXLogic = nPosX;
1222                 if (bWorksInPixels)
1223                     nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
1224 
1225                 drawCells(rRenderContext, std::optional<Color>(), nullptr, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, nullptr, pOldDataBarInfo, nullptr, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
1226 
1227                 nArrY += nSkip;
1228 
1229                 if (bTaggedPDF)
1230                     pPDF->EndStructureElement();
1231             }
1232         }
1233         nPosY += nRowHeight;
1234     }
1235 }
1236 
DrawShadow()1237 void ScOutputData::DrawShadow()
1238 {
1239     DrawExtraShadow( false, false, false, false );
1240 }
1241 
DrawExtraShadow(bool bLeft,bool bTop,bool bRight,bool bBottom)1242 void ScOutputData::DrawExtraShadow(bool bLeft, bool bTop, bool bRight, bool bBottom)
1243 {
1244     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1245     bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1246 
1247     mpDev->SetLineColor();
1248 
1249     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1250     bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
1251     Color aAutoTextColor;
1252     if ( bCellContrast )
1253         aAutoTextColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
1254 
1255     tools::Long nInitPosX = mnScrX;
1256     if ( mbLayoutRTL )
1257     {
1258         Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
1259         tools::Long nOneX = aOnePixel.Width();
1260         nInitPosX += mnMirrorW - nOneX;
1261     }
1262     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
1263 
1264     tools::Long nPosY = mnScrY - mpRowInfo[0].nHeight;
1265     for (SCSIZE nArrY=0; nArrY<mnArrCount; nArrY++)
1266     {
1267         bool bCornerY = ( nArrY == 0 ) || ( nArrY+1 == mnArrCount );
1268         bool bSkipY = ( nArrY==0 && !bTop ) || ( nArrY+1 == mnArrCount && !bBottom );
1269 
1270         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1271         tools::Long nRowHeight = pThisRowInfo->nHeight;
1272 
1273         if ( pThisRowInfo->bChanged && !bSkipY )
1274         {
1275             tools::Long nPosX = nInitPosX - mpRowInfo[0].basicCellInfo(mnX1-1).nWidth * nLayoutSign;
1276             for (SCCOL nCol=mnX1-1; nCol<=mnX2+1; nCol++)
1277             {
1278                 bool bCornerX = ( nCol==mnX1-1 || nCol==mnX2+1 );
1279                 bool bSkipX = ( nCol==mnX1-1 && !bLeft ) || ( nCol==mnX2+1 && !bRight );
1280 
1281                 for (sal_uInt16 nPass=0; nPass<2; nPass++) // horizontal / vertical
1282                 {
1283                     const SvxShadowItem* pAttr = nPass ?
1284                             pThisRowInfo->cellInfo(nCol).pVShadowOrigin :
1285                             pThisRowInfo->cellInfo(nCol).pHShadowOrigin;
1286                     if ( pAttr && !bSkipX )
1287                     {
1288                         if (bTaggedPDF)
1289                             pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::NonStructElement);
1290 
1291                         ScShadowPart ePart = nPass ?
1292                                 pThisRowInfo->cellInfo(nCol).eVShadowPart :
1293                                 pThisRowInfo->cellInfo(nCol).eHShadowPart;
1294 
1295                         bool bDo = true;
1296                         if ( (nPass==0 && bCornerX) || (nPass==1 && bCornerY) )
1297                             if ( ePart != SC_SHADOW_CORNER )
1298                                 bDo = false;
1299 
1300                         if (bDo)
1301                         {
1302                             tools::Long nThisWidth = mpRowInfo[0].basicCellInfo(nCol).nWidth;
1303                             tools::Long nMaxWidth = nThisWidth;
1304                             if (!nMaxWidth)
1305                             {
1306                                 //! direction must depend on shadow location
1307                                 SCCOL nWx = nCol+1;
1308                                 while (nWx<mnX2 && !mpRowInfo[0].basicCellInfo(nWx).nWidth)
1309                                     ++nWx;
1310                                 nMaxWidth = mpRowInfo[0].basicCellInfo(nWx).nWidth;
1311                             }
1312 
1313                             // rectangle is in logical orientation
1314                             tools::Rectangle aRect( nPosX, nPosY,
1315                                              nPosX + ( nThisWidth - 1 ) * nLayoutSign,
1316                                              nPosY + mpRowInfo[nArrY].nHeight - 1 );
1317 
1318                             tools::Long nSize = pAttr->GetWidth();
1319                             tools::Long nSizeX = static_cast<tools::Long>(nSize*mnPPTX);
1320                             if (nSizeX >= nMaxWidth) nSizeX = nMaxWidth-1;
1321                             tools::Long nSizeY = static_cast<tools::Long>(nSize*mnPPTY);
1322                             if (nSizeY >= nRowHeight) nSizeY = nRowHeight-1;
1323 
1324                             nSizeX *= nLayoutSign;      // used only to add to rectangle values
1325 
1326                             SvxShadowLocation eLoc = pAttr->GetLocation();
1327                             if ( mbLayoutRTL )
1328                             {
1329                                 //  Shadow location is specified as "visual" (right is always right),
1330                                 //  so the attribute's location value is mirrored here and in FillInfo.
1331                                 switch (eLoc)
1332                                 {
1333                                     case SvxShadowLocation::BottomRight: eLoc = SvxShadowLocation::BottomLeft;  break;
1334                                     case SvxShadowLocation::BottomLeft:  eLoc = SvxShadowLocation::BottomRight; break;
1335                                     case SvxShadowLocation::TopRight:    eLoc = SvxShadowLocation::TopLeft;     break;
1336                                     case SvxShadowLocation::TopLeft:     eLoc = SvxShadowLocation::TopRight;    break;
1337                                     default:
1338                                     {
1339                                         // added to avoid warnings
1340                                     }
1341                                 }
1342                             }
1343 
1344                             if (ePart == SC_SHADOW_HORIZ || ePart == SC_SHADOW_HSTART ||
1345                                 ePart == SC_SHADOW_CORNER)
1346                             {
1347                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
1348                                     aRect.SetTop( aRect.Bottom() - nSizeY );
1349                                 else
1350                                     aRect.SetBottom( aRect.Top() + nSizeY );
1351                             }
1352                             if (ePart == SC_SHADOW_VERT || ePart == SC_SHADOW_VSTART ||
1353                                 ePart == SC_SHADOW_CORNER)
1354                             {
1355                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
1356                                     aRect.SetLeft( aRect.Right() - nSizeX );
1357                                 else
1358                                     aRect.SetRight( aRect.Left() + nSizeX );
1359                             }
1360                             if (ePart == SC_SHADOW_HSTART)
1361                             {
1362                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
1363                                     aRect.AdjustRight( -nSizeX );
1364                                 else
1365                                     aRect.AdjustLeft(nSizeX );
1366                             }
1367                             if (ePart == SC_SHADOW_VSTART)
1368                             {
1369                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
1370                                     aRect.AdjustBottom( -nSizeY );
1371                                 else
1372                                     aRect.AdjustTop(nSizeY );
1373                             }
1374 
1375                             //! merge rectangles?
1376                             mpDev->SetFillColor( bCellContrast ? aAutoTextColor : pAttr->GetColor() );
1377                             mpDev->DrawRect( aRect );
1378 
1379                             if (bTaggedPDF)
1380                                 pPDF->EndStructureElement();
1381                         }
1382                     }
1383                 }
1384 
1385                 nPosX += mpRowInfo[0].basicCellInfo(nCol).nWidth * nLayoutSign;
1386             }
1387         }
1388         nPosY += nRowHeight;
1389     }
1390 }
1391 
DrawClear()1392 void ScOutputData::DrawClear()
1393 {
1394     tools::Rectangle aRect;
1395     Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
1396     tools::Long nOneX = aOnePixel.Width();
1397     tools::Long nOneY = aOnePixel.Height();
1398 
1399     // (called only for ScGridWindow)
1400     Color aBgColor(ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
1401 
1402     if (mbMetaFile)
1403         nOneX = nOneY = 0;
1404 
1405     mpDev->SetLineColor();
1406 
1407     mpDev->SetFillColor( aBgColor );
1408 
1409     tools::Long nPosY = mnScrY;
1410     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
1411     {
1412         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1413         tools::Long nRowHeight = pThisRowInfo->nHeight;
1414 
1415         if ( pThisRowInfo->bChanged )
1416         {
1417             // scan for more rows which must be painted:
1418             SCSIZE nSkip = 0;
1419             while ( nArrY+nSkip+2<mnArrCount && mpRowInfo[nArrY+nSkip+1].bChanged )
1420             {
1421                 ++nSkip;
1422                 nRowHeight += mpRowInfo[nArrY+nSkip].nHeight;    // after incrementing
1423             }
1424 
1425             aRect = tools::Rectangle( Point( mnScrX, nPosY ),
1426                     Size( mnScrW+1-nOneX, nRowHeight+1-nOneY) );
1427             mpDev->DrawRect( aRect );
1428 
1429             nArrY += nSkip;
1430         }
1431         nPosY += nRowHeight;
1432     }
1433 }
1434 
1435 // Lines
1436 
lclGetSnappedX(const OutputDevice & rDev,tools::Long nPosX,bool mbSnapPixel)1437 static tools::Long lclGetSnappedX( const OutputDevice& rDev, tools::Long nPosX, bool mbSnapPixel )
1438 {
1439     return (mbSnapPixel && nPosX) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( nPosX, 0 ) ) ).Width() : nPosX;
1440 }
1441 
lclGetSnappedY(const OutputDevice & rDev,tools::Long nPosY,bool mbSnapPixel)1442 static tools::Long lclGetSnappedY( const OutputDevice& rDev, tools::Long nPosY, bool mbSnapPixel )
1443 {
1444     return (mbSnapPixel && nPosY) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( 0, nPosY ) ) ).Height() : nPosY;
1445 }
1446 
DrawFrame(vcl::RenderContext & rRenderContext)1447 void ScOutputData::DrawFrame(vcl::RenderContext& rRenderContext)
1448 {
1449     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1450     bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1451     if (bTaggedPDF)
1452         pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::NonStructElement);
1453 
1454     DrawModeFlags nOldDrawMode = rRenderContext.GetDrawMode();
1455 
1456     Color aSingleColor;
1457     bool bUseSingleColor = false;
1458     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1459     bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
1460 
1461     //  if a Calc OLE object is embedded in Draw/Impress, the VCL DrawMode is used
1462     //  for display mode / B&W printing. The VCL DrawMode handling doesn't work for lines
1463     //  that are drawn with DrawRect, so if the line/background bits are set, the DrawMode
1464     //  must be reset and the border colors handled here.
1465 
1466     if ( ( nOldDrawMode & DrawModeFlags::WhiteFill ) && ( nOldDrawMode & DrawModeFlags::BlackLine ) )
1467     {
1468         rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::WhiteFill) );
1469         aSingleColor = COL_BLACK;
1470         bUseSingleColor = true;
1471     }
1472     else if ( ( nOldDrawMode & DrawModeFlags::SettingsFill ) && ( nOldDrawMode & DrawModeFlags::SettingsLine ) )
1473     {
1474         rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::SettingsFill) );
1475         aSingleColor = rStyleSettings.GetWindowTextColor();     // same as used in VCL for DrawModeFlags::SettingsLine
1476         bUseSingleColor = true;
1477     }
1478     else if ( bCellContrast )
1479     {
1480         aSingleColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
1481         bUseSingleColor = true;
1482     }
1483 
1484     const Color* pForceColor = bUseSingleColor ? &aSingleColor : nullptr;
1485 
1486     if (mrTabInfo.maArray.HasCellRotation())
1487     {
1488         DrawRotatedFrame(rRenderContext);        // removes the lines that must not be painted here
1489     }
1490 
1491     tools::Long nInitPosX = mnScrX;
1492     if ( mbLayoutRTL )
1493     {
1494         Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
1495         tools::Long nOneX = aOnePixel.Width();
1496         nInitPosX += mnMirrorW - nOneX;
1497     }
1498     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
1499 
1500     // *** set column and row sizes of the frame border array ***
1501 
1502     svx::frame::Array& rArray = mrTabInfo.maArray;
1503     size_t nColCount = rArray.GetColCount();
1504     size_t nRowCount = rArray.GetRowCount();
1505 
1506     // row heights
1507 
1508     // row 0 is not visible (dummy for borders from top) - subtract its height from initial position
1509     // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit before
1510     tools::Long nOldPosY = mnScrY - 1 - mpRowInfo[ 0 ].nHeight;
1511     tools::Long nOldSnapY = lclGetSnappedY( rRenderContext, nOldPosY, mbSnapPixel );
1512     rArray.SetYOffset( nOldSnapY );
1513     for( size_t nRow = 0; nRow < nRowCount; ++nRow )
1514     {
1515         tools::Long nNewPosY = nOldPosY + mpRowInfo[ nRow ].nHeight;
1516         tools::Long nNewSnapY = lclGetSnappedY( rRenderContext, nNewPosY, mbSnapPixel );
1517         rArray.SetRowHeight( nRow, nNewSnapY - nOldSnapY );
1518         nOldPosY = nNewPosY;
1519         nOldSnapY = nNewSnapY;
1520     }
1521 
1522     // column widths
1523 
1524     // column mnX1-1 is not visible (dummy for borders from left) - subtract its width from initial position
1525     // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit above
1526     tools::Long nOldPosX = nInitPosX - nLayoutSign * (1 + mpRowInfo[ 0 ].basicCellInfo( mnX1 - 1 ).nWidth);
1527     tools::Long nOldSnapX = lclGetSnappedX( rRenderContext, nOldPosX, mbSnapPixel );
1528     // set X offset for left-to-right sheets; for right-to-left sheets this is done after for() loop
1529     if( !mbLayoutRTL )
1530         rArray.SetXOffset( nOldSnapX );
1531     for( SCCOL nCol = mnX1 - 1; nCol <= mnX2 + 1; ++nCol )
1532     {
1533         size_t nArrCol = mbLayoutRTL ? mnX2 + 1 - nCol : nCol - (mnX1 - 1);
1534         tools::Long nNewPosX = nOldPosX + mpRowInfo[ 0 ].basicCellInfo( nCol ).nWidth * nLayoutSign;
1535         tools::Long nNewSnapX = lclGetSnappedX( rRenderContext, nNewPosX, mbSnapPixel );
1536         rArray.SetColWidth( nArrCol, std::abs( nNewSnapX - nOldSnapX ) );
1537         nOldPosX = nNewPosX;
1538         nOldSnapX = nNewSnapX;
1539     }
1540     if( mbLayoutRTL )
1541         rArray.SetXOffset( nOldSnapX );
1542 
1543     // *** draw the array ***
1544 
1545     size_t nFirstCol = 1;
1546     size_t nFirstRow = 1;
1547     size_t nLastCol = nColCount - 2;
1548     size_t nLastRow = nRowCount - 2;
1549 
1550     if( mrTabInfo.mbPageMode )
1551         rArray.SetClipRange( nFirstCol, nFirstRow, nLastCol, nLastRow );
1552 
1553     // draw only rows with set RowInfo::bChanged flag
1554     size_t nRow1 = nFirstRow;
1555     std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D());
1556     if (!pProcessor)
1557         return;
1558     while( nRow1 <= nLastRow )
1559     {
1560         while( (nRow1 <= nLastRow) && !mpRowInfo[ nRow1 ].bChanged ) ++nRow1;
1561         if( nRow1 <= nLastRow )
1562         {
1563             size_t nRow2 = nRow1;
1564             while( (nRow2 + 1 <= nLastRow) && mpRowInfo[ nRow2 + 1 ].bChanged ) ++nRow2;
1565             auto xPrimitive = rArray.CreateB2DPrimitiveRange(
1566                     nFirstCol, nRow1, nLastCol, nRow2, pForceColor );
1567             pProcessor->process(xPrimitive);
1568             nRow1 = nRow2 + 1;
1569         }
1570     }
1571     pProcessor.reset();
1572 
1573     rRenderContext.SetDrawMode(nOldDrawMode);
1574 
1575     if (bTaggedPDF)
1576         pPDF->EndStructureElement();
1577 }
1578 
DrawRotatedFrame(vcl::RenderContext & rRenderContext)1579 void ScOutputData::DrawRotatedFrame(vcl::RenderContext& rRenderContext)
1580 {
1581     //! save nRotMax
1582     SCCOL nRotMax = mnX2;
1583     for (SCSIZE nRotY=0; nRotY<mnArrCount; nRotY++)
1584         if (mpRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && mpRowInfo[nRotY].nRotMaxCol > nRotMax)
1585             nRotMax = mpRowInfo[nRotY].nRotMaxCol;
1586 
1587     const ScPatternAttr* pPattern;
1588     const SfxItemSet*    pCondSet;
1589 
1590     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1591     bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
1592 
1593     tools::Long nInitPosX = mnScrX;
1594     if ( mbLayoutRTL )
1595     {
1596         Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
1597         tools::Long nOneX = aOnePixel.Width();
1598         nInitPosX += mnMirrorW - nOneX;
1599     }
1600     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
1601 
1602     tools::Rectangle aClipRect( Point(mnScrX, mnScrY), Size(mnScrW, mnScrH) );
1603     if (mbMetaFile)
1604     {
1605         rRenderContext.Push();
1606         rRenderContext.IntersectClipRegion( aClipRect );
1607     }
1608     else
1609         rRenderContext.SetClipRegion( vcl::Region( aClipRect ) );
1610 
1611     std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D( ));
1612     tools::Long nPosY = mnScrY;
1613     for (SCSIZE nArrY=1; nArrY<mnArrCount; nArrY++)
1614     {
1615         // Rotated is also drawn one line above/below Changed if parts extend into the cell
1616 
1617         RowInfo& rPrevRowInfo = mpRowInfo[nArrY-1];
1618         RowInfo& rThisRowInfo = mpRowInfo[nArrY];
1619         RowInfo& rNextRowInfo = mpRowInfo[nArrY+1];
1620 
1621         tools::Long nRowHeight = rThisRowInfo.nHeight;
1622         if ( rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE &&
1623              ( rThisRowInfo.bChanged || rPrevRowInfo.bChanged ||
1624                ( nArrY+1<mnArrCount && rNextRowInfo.bChanged ) ) )
1625         {
1626             SCROW nY = rThisRowInfo.nRowNo;
1627             tools::Long nPosX = 0;
1628             SCCOL nX;
1629             for (nX=0; nX<=nRotMax; nX++)
1630             {
1631                 if (nX==mnX1) nPosX = nInitPosX;     // calculated individually for preceding positions
1632 
1633                 ScCellInfo* pInfo = &rThisRowInfo.cellInfo(nX);
1634                 tools::Long nColWidth = mpRowInfo[0].basicCellInfo(nX).nWidth;
1635                 if ( pInfo->nRotateDir > ScRotateDir::Standard &&
1636                         !pInfo->bHOverlapped && !pInfo->bVOverlapped )
1637                 {
1638                     pPattern = pInfo->pPatternAttr;
1639                     pCondSet = pInfo->pConditionSet;
1640                     if (!pPattern)
1641                     {
1642                         pPattern = mpDoc->GetPattern( nX, nY, mnTab );
1643                         pInfo->pPatternAttr = pPattern;
1644                         pCondSet = mpDoc->GetCondResult( nX, nY, mnTab );
1645                         pInfo->pConditionSet = pCondSet;
1646                     }
1647 
1648                     //! LastPattern etc.
1649 
1650                     Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
1651                     SvxRotateMode eRotMode =
1652                                     pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
1653 
1654                     if (nAttrRotate)
1655                     {
1656                         if (nX < mnX1)         // compute negative position
1657                         {
1658                             nPosX = nInitPosX;
1659                             SCCOL nCol = mnX1;
1660                             while (nCol > nX)
1661                             {
1662                                 --nCol;
1663                                 nPosX -= nLayoutSign * static_cast<tools::Long>(mpRowInfo[0].basicCellInfo(nCol).nWidth);
1664                             }
1665                         }
1666 
1667                         // start position minus 1 so rotated backgrounds suit the border
1668                         // (border is on the grid)
1669 
1670                         tools::Long nTop = nPosY - 1;
1671                         tools::Long nBottom = nPosY + nRowHeight - 1;
1672                         tools::Long nTopLeft = nPosX - nLayoutSign;
1673                         tools::Long nTopRight = nPosX + (nColWidth - 1) * nLayoutSign;
1674                         tools::Long nBotLeft = nTopLeft;
1675                         tools::Long nBotRight = nTopRight;
1676 
1677                         // inclusion of the sign here hasn't been decided yet
1678                         // (if not, the extension of the non-rotated background must also be changed)
1679                         double nRealOrient = nLayoutSign * toRadians(nAttrRotate);     // 1/100th degrees
1680                         double nCos = cos(nRealOrient);
1681                         double nSin = sin(nRealOrient);
1682                         //! restrict !!!
1683                         tools::Long nSkew = static_cast<tools::Long>(nRowHeight * nCos / nSin);
1684 
1685                         switch (eRotMode)
1686                         {
1687                         case SVX_ROTATE_MODE_BOTTOM:
1688                             nTopLeft += nSkew;
1689                             nTopRight += nSkew;
1690                             break;
1691                         case SVX_ROTATE_MODE_CENTER:
1692                             nSkew /= 2;
1693                             nTopLeft += nSkew;
1694                             nTopRight += nSkew;
1695                             nBotLeft -= nSkew;
1696                             nBotRight -= nSkew;
1697                             break;
1698                         case SVX_ROTATE_MODE_TOP:
1699                             nBotLeft -= nSkew;
1700                             nBotRight -= nSkew;
1701                             break;
1702                         default:
1703                         {
1704                             // added to avoid warnings
1705                         }
1706                         }
1707 
1708                         Point aPoints[4];
1709                         aPoints[0] = Point(nTopLeft, nTop);
1710                         aPoints[1] = Point(nTopRight, nTop);
1711                         aPoints[2] = Point(nBotRight, nBottom);
1712                         aPoints[3] = Point(nBotLeft, nBottom);
1713 
1714                         const SvxBrushItem* pBackground(static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem()));
1715                         if (!pBackground)
1716                             pBackground = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
1717                         if (bCellContrast)
1718                         {
1719                             //  high contrast for cell borders and backgrounds -> empty background
1720                             pBackground = ScGlobal::GetEmptyBrushItem();
1721                         }
1722                         if (!pInfo->mxColorScale)
1723                         {
1724                             const Color& rColor = pBackground->GetColor();
1725                             if (rColor.GetAlpha() != 0)
1726                             {
1727                                 //  draw background only for the changed row itself
1728                                 //  (background doesn't extend into other cells).
1729                                 //  For the borders (rotated and normal), clipping should be
1730                                 //  set if the row isn't changed, but at least the borders
1731                                 //  don't cover the cell contents.
1732                                 if (rThisRowInfo.bChanged)
1733                                 {
1734                                     tools::Polygon aPoly(4, aPoints);
1735 
1736                                     // for DrawPolygon, without Pen one pixel is left out
1737                                     // to the right and below...
1738                                     if (!rColor.IsTransparent())
1739                                         rRenderContext.SetLineColor(rColor);
1740                                     else
1741                                         rRenderContext.SetLineColor();
1742                                     rRenderContext.SetFillColor(rColor);
1743                                     rRenderContext.DrawPolygon(aPoly);
1744                                 }
1745                             }
1746                         }
1747                         else
1748                         {
1749                             tools::Polygon aPoly(4, aPoints);
1750                             std::optional<Color> const & pColor = pInfo->mxColorScale;
1751 
1752                             // for DrawPolygon, without Pen one pixel is left out
1753                             // to the right and below...
1754                             if (!pColor->IsTransparent())
1755                                 rRenderContext.SetLineColor(*pColor);
1756                             else
1757                                 rRenderContext.SetLineColor();
1758                             rRenderContext.SetFillColor(*pColor);
1759                             rRenderContext.DrawPolygon(aPoly);
1760 
1761                         }
1762                     }
1763                 }
1764                 nPosX += nColWidth * nLayoutSign;
1765             }
1766         }
1767         nPosY += nRowHeight;
1768     }
1769 
1770     pProcessor.reset();
1771 
1772     if (mbMetaFile)
1773         rRenderContext.Pop();
1774     else
1775         rRenderContext.SetClipRegion();
1776 }
1777 
CreateProcessor2D()1778 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> ScOutputData::CreateProcessor2D( )
1779 {
1780     mpDoc->InitDrawLayer(mpDoc->GetDocumentShell());
1781     ScDrawLayer* pDrawLayer = mpDoc->GetDrawLayer();
1782     if (!pDrawLayer)
1783         return nullptr;
1784 
1785     basegfx::B2DRange aViewRange;
1786     SdrPage *pDrawPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( mnTab ) );
1787     drawinglayer::geometry::ViewInformation2D aNewViewInfos;
1788     aNewViewInfos.setViewTransformation(mpDev->GetViewTransformation());
1789     aNewViewInfos.setViewport(aViewRange);
1790     aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
1791 
1792     return drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1793                     *mpDev, aNewViewInfos );
1794 }
1795 
1796 // Printer
1797 
GetChangedAreaRegion()1798 vcl::Region ScOutputData::GetChangedAreaRegion()
1799 {
1800     vcl::Region aRegion;
1801     tools::Rectangle aDrawingRect;
1802     bool bHad(false);
1803     tools::Long nPosY = mnScrY;
1804     SCSIZE nArrY;
1805 
1806     aDrawingRect.SetLeft( mnScrX );
1807     aDrawingRect.SetRight( mnScrX+mnScrW-1 );
1808 
1809     for(nArrY=1; nArrY+1<mnArrCount; nArrY++)
1810     {
1811         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1812 
1813         if(pThisRowInfo->bChanged)
1814         {
1815             if(!bHad)
1816             {
1817                 aDrawingRect.SetTop( nPosY );
1818                 bHad = true;
1819             }
1820 
1821             aDrawingRect.SetBottom( nPosY + mpRowInfo[nArrY].nHeight - 1 );
1822         }
1823         else if(bHad)
1824         {
1825             aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
1826             bHad = false;
1827         }
1828 
1829         nPosY += mpRowInfo[nArrY].nHeight;
1830     }
1831 
1832     if(bHad)
1833     {
1834         aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
1835     }
1836 
1837     return aRegion;
1838 }
1839 
SetChangedClip()1840 bool ScOutputData::SetChangedClip()
1841 {
1842     tools::PolyPolygon aPoly;
1843 
1844     tools::Rectangle aDrawingRect;
1845     aDrawingRect.SetLeft( mnScrX );
1846     aDrawingRect.SetRight( mnScrX+mnScrW-1 );
1847 
1848     bool    bHad    = false;
1849     tools::Long    nPosY   = mnScrY;
1850     SCSIZE  nArrY;
1851     for (nArrY=1; nArrY+1<mnArrCount; nArrY++)
1852     {
1853         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1854 
1855         if ( pThisRowInfo->bChanged )
1856         {
1857             if (!bHad)
1858             {
1859                 aDrawingRect.SetTop( nPosY );
1860                 bHad = true;
1861             }
1862             aDrawingRect.SetBottom( nPosY + mpRowInfo[nArrY].nHeight - 1 );
1863         }
1864         else if (bHad)
1865         {
1866             aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
1867             bHad = false;
1868         }
1869         nPosY += mpRowInfo[nArrY].nHeight;
1870     }
1871 
1872     if (bHad)
1873         aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
1874 
1875     bool bRet = (aPoly.Count() != 0);
1876     if (bRet)
1877         mpDev->SetClipRegion(vcl::Region(aPoly));
1878     return bRet;
1879 }
1880 
FindChanged()1881 void ScOutputData::FindChanged()
1882 {
1883     SCCOL   nX;
1884     SCSIZE  nArrY;
1885 
1886     bool bWasIdleEnabled = mpDoc->IsIdleEnabled();
1887     mpDoc->EnableIdle(false);
1888     for (nArrY=0; nArrY<mnArrCount; nArrY++)
1889         mpRowInfo[nArrY].bChanged = false;
1890 
1891     SCCOL nCol1 = mpDoc->MaxCol(), nCol2 = 0;
1892     SCROW nRow1 = mpDoc->MaxRow(), nRow2 = 0;
1893     bool bAnyDirty = false;
1894     bool bAnyChanged = false;
1895 
1896     for (nArrY=0; nArrY<mnArrCount; nArrY++)
1897     {
1898         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1899         for (nX=mnX1; nX<=mnX2; nX++)
1900         {
1901             const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
1902 
1903             if (rCell.getType() != CELLTYPE_FORMULA)
1904                 continue;
1905 
1906             ScFormulaCell* pFCell = rCell.getFormula();
1907             if (pFCell->IsRunning())
1908                 // still being interpreted. Skip it.
1909                 continue;
1910 
1911             bool bDirty = pFCell->GetDirty();
1912             bAnyChanged = bAnyChanged || pFCell->IsChanged();
1913 
1914             if (bDirty)
1915             {
1916                 if (!bAnyDirty)
1917                 {
1918                     ScProgress::CreateInterpretProgress(mpDoc);
1919                     bAnyDirty = true;
1920                 }
1921 
1922                 ScAddress& rPos(pFCell->aPos);
1923                 nCol1 = std::min(rPos.Col(), nCol1);
1924                 nCol2 = std::max(rPos.Col(), nCol2);
1925                 nRow1 = std::min(rPos.Row(), nRow1);
1926                 nRow2 = std::max(rPos.Row(), nRow2);
1927 
1928                 const SfxUInt32Item* pItem = mpDoc->GetAttr(rPos, ATTR_VALIDDATA);
1929                 const ScValidationData* pData = mpDoc->GetValidationEntry(pItem->GetValue());
1930                 if (pData)
1931                 {
1932                     ScRefCellValue aCell(*mpDoc, rPos);
1933                     if (pData->IsDataValid(aCell, rPos))
1934                         ScDetectiveFunc(*mpDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
1935                 }
1936             }
1937         }
1938     }
1939 
1940     if (bAnyDirty || bAnyChanged)
1941     {
1942         if (bAnyDirty)
1943             mpDoc->EnsureFormulaCellResults(ScRange(nCol1, nRow1, mnTab, nCol2, nRow2, mnTab), true);
1944 
1945         for (nArrY=0; nArrY<mnArrCount; nArrY++)
1946         {
1947             RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
1948             for (nX=mnX1; nX<=mnX2; nX++)
1949             {
1950                 const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
1951 
1952                 if (rCell.getType() != CELLTYPE_FORMULA)
1953                     continue;
1954 
1955                 ScFormulaCell* pFCell = rCell.getFormula();
1956                 if (pFCell->IsRunning())
1957                     // still being interpreted. Skip it.
1958                     continue;
1959 
1960                 if (!pFCell->IsChanged())
1961                     // the result hasn't changed. Skip it.
1962                     continue;
1963 
1964                 pThisRowInfo->bChanged = true;
1965                 if ( pThisRowInfo->cellInfo(nX).bMerged )
1966                 {
1967                     SCSIZE nOverY = nArrY + 1;
1968                     while ( nOverY<mnArrCount &&
1969                             mpRowInfo[nOverY].cellInfo(nX).bVOverlapped )
1970                     {
1971                         mpRowInfo[nOverY].bChanged = true;
1972                         ++nOverY;
1973                     }
1974                 }
1975             }
1976         }
1977 
1978         if (bAnyDirty)
1979             ScProgress::DeleteInterpretProgress();
1980     }
1981 
1982     mpDoc->EnableIdle(bWasIdleEnabled);
1983 }
1984 
FillReferenceMark(SCCOL nRefStartX,SCROW nRefStartY,SCCOL nRefEndX,SCROW nRefEndY,const Color & rColor)1985 ReferenceMark ScOutputData::FillReferenceMark( SCCOL nRefStartX, SCROW nRefStartY,
1986                                 SCCOL nRefEndX, SCROW nRefEndY, const Color& rColor)
1987 {
1988     ReferenceMark aResult;
1989 
1990     PutInOrder( nRefStartX, nRefEndX );
1991     PutInOrder( nRefStartY, nRefEndY );
1992 
1993     if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
1994         mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, mnTab );
1995 
1996     if ( nRefStartX <= mnVisX2 && nRefEndX >= mnVisX1 &&
1997          nRefStartY <= mnVisY2 && nRefEndY >= mnVisY1 )
1998     {
1999         tools::Long nMinX = mnScrX;
2000         tools::Long nMinY = mnScrY;
2001         tools::Long nMaxX = mnScrX + mnScrW - 1;
2002         tools::Long nMaxY = mnScrY + mnScrH - 1;
2003         if ( mbLayoutRTL )
2004             std::swap( nMinX, nMaxX );
2005         tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2006 
2007         bool bTop    = false;
2008         bool bBottom = false;
2009         bool bLeft   = false;
2010         bool bRight  = false;
2011 
2012         tools::Long nPosY = mnScrY;
2013         bool bNoStartY = ( mnY1 < nRefStartY );
2014         bool bNoEndY   = false;
2015         for (SCSIZE nArrY=1; nArrY<mnArrCount; nArrY++)      // loop to end for bNoEndY check
2016         {
2017             SCROW nY = mpRowInfo[nArrY].nRowNo;
2018 
2019             if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
2020             {
2021                 nMinY = nPosY;
2022                 bTop = true;
2023             }
2024             if ( nY==nRefEndY )
2025             {
2026                 nMaxY = nPosY + mpRowInfo[nArrY].nHeight - 2;
2027                 bBottom = true;
2028             }
2029             if ( nY>nRefEndY && bNoEndY )
2030             {
2031                 nMaxY = nPosY-2;
2032                 bBottom = true;
2033             }
2034             bNoStartY = ( nY < nRefStartY );
2035             bNoEndY   = ( nY < nRefEndY );
2036             nPosY += mpRowInfo[nArrY].nHeight;
2037         }
2038 
2039         tools::Long nPosX = mnScrX;
2040         if ( mbLayoutRTL )
2041             nPosX += mnMirrorW - 1;      // always in pixels
2042 
2043         for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2044         {
2045             if ( nX==nRefStartX )
2046             {
2047                 nMinX = nPosX;
2048                 bLeft = true;
2049             }
2050             if ( nX==nRefEndX )
2051             {
2052                 nMaxX = nPosX + ( mpRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
2053                 bRight = true;
2054             }
2055             nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2056         }
2057 
2058         if (bTop && bBottom && bLeft && bRight)
2059         {
2060             // mnPPT[XY] already has the factor aZoom[XY] in it.
2061             aResult = ReferenceMark( nMinX / mnPPTX,
2062                                      nMinY / mnPPTY,
2063                                      ( nMaxX - nMinX ) / mnPPTX,
2064                                      ( nMaxY - nMinY ) / mnPPTY,
2065                                      mnTab,
2066                                      rColor );
2067         }
2068     }
2069 
2070     return aResult;
2071 }
2072 
DrawRefMark(SCCOL nRefStartX,SCROW nRefStartY,SCCOL nRefEndX,SCROW nRefEndY,const Color & rColor,bool bHandle)2073 void ScOutputData::DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
2074                                 SCCOL nRefEndX, SCROW nRefEndY,
2075                                 const Color& rColor, bool bHandle )
2076 {
2077     PutInOrder( nRefStartX, nRefEndX );
2078     PutInOrder( nRefStartY, nRefEndY );
2079 
2080     if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
2081         mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, mnTab );
2082     else if (mpDoc->ValidCol(nRefEndX) && mpDoc->ValidRow(nRefEndY) &&
2083              mpDoc->HasAttrib(nRefEndX, nRefEndY, mnTab, HasAttrFlags::Merged))
2084         mpDoc->ExtendMerge(nRefEndX, nRefEndY, nRefEndX, nRefEndY, mnTab);
2085 
2086     if ( !(nRefStartX <= mnVisX2 && nRefEndX >= mnVisX1 &&
2087          nRefStartY <= mnVisY2 && nRefEndY >= mnVisY1) )
2088         return;
2089 
2090     tools::Long nMinX = mnScrX;
2091     tools::Long nMinY = mnScrY;
2092     tools::Long nMaxX = mnScrX + mnScrW - 1;
2093     tools::Long nMaxY = mnScrY + mnScrH - 1;
2094     if ( mbLayoutRTL )
2095         std::swap( nMinX, nMaxX );
2096     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2097 
2098     bool bTop    = false;
2099     bool bBottom = false;
2100     bool bLeft   = false;
2101     bool bRight  = false;
2102 
2103     tools::Long nPosY = mnScrY;
2104     bool bNoStartY = ( mnY1 < nRefStartY );
2105     bool bNoEndY   = false;
2106     for (SCSIZE nArrY=1; nArrY<mnArrCount; nArrY++)      // loop to end for bNoEndY check
2107     {
2108         SCROW nY = mpRowInfo[nArrY].nRowNo;
2109 
2110         if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
2111         {
2112             nMinY = nPosY;
2113             bTop = true;
2114         }
2115         if ( nY==nRefEndY )
2116         {
2117             nMaxY = nPosY + mpRowInfo[nArrY].nHeight - 2;
2118             bBottom = true;
2119         }
2120         if ( nY>nRefEndY && bNoEndY )
2121         {
2122             nMaxY = nPosY-2;
2123             bBottom = true;
2124         }
2125         bNoStartY = ( nY < nRefStartY );
2126         bNoEndY   = ( nY < nRefEndY );
2127         nPosY += mpRowInfo[nArrY].nHeight;
2128     }
2129 
2130     tools::Long nPosX = mnScrX;
2131     if ( mbLayoutRTL )
2132         nPosX += mnMirrorW - 1;      // always in pixels
2133 
2134     for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2135     {
2136         if ( nX==nRefStartX )
2137         {
2138             nMinX = nPosX;
2139             bLeft = true;
2140         }
2141         if ( nX==nRefEndX )
2142         {
2143             nMaxX = nPosX + ( mpRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
2144             bRight = true;
2145         }
2146         nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2147     }
2148 
2149     if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
2150         return;
2151 
2152     mpDev->SetLineColor( rColor );
2153     if (bTop && bBottom && bLeft && bRight && !comphelper::LibreOfficeKit::isActive() )
2154     {
2155             mpDev->SetFillColor();
2156             mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
2157     }
2158     else if ( !comphelper::LibreOfficeKit::isActive() )
2159     {
2160         if (bTop)
2161             mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMaxX, nMinY ) );
2162         if (bBottom)
2163             mpDev->DrawLine( Point( nMinX, nMaxY ), Point( nMaxX, nMaxY ) );
2164         if (bLeft)
2165             mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMinX, nMaxY ) );
2166         if (bRight)
2167             mpDev->DrawLine( Point( nMaxX, nMinY ), Point( nMaxX, nMaxY ) );
2168     }
2169     if ( !bHandle || !bRight || !bBottom || comphelper::LibreOfficeKit::isActive() )
2170         return;
2171 
2172     mpDev->SetLineColor( rColor );
2173     mpDev->SetFillColor( rColor );
2174 
2175     const sal_Int32 aRadius = 4;
2176 
2177     sal_Int32 aRectMaxX1 = nMaxX - nLayoutSign * aRadius;
2178     sal_Int32 aRectMaxX2 = nMaxX + nLayoutSign;
2179     sal_Int32 aRectMinX1 = nMinX - nLayoutSign;
2180     sal_Int32 aRectMinX2 = nMinX + nLayoutSign * aRadius;
2181 
2182     sal_Int32 aRectMaxY1 = nMaxY - aRadius;
2183     sal_Int32 aRectMaxY2 = nMaxY + 1;
2184     sal_Int32 aRectMinY1 = nMinY - 1;
2185     sal_Int32 aRectMinY2 = nMinY + aRadius;
2186 
2187     // Draw corner rectangles
2188     tools::Rectangle aLowerRight( aRectMaxX1, aRectMaxY1, aRectMaxX2, aRectMaxY2 );
2189     tools::Rectangle aUpperLeft ( aRectMinX1, aRectMinY1, aRectMinX2, aRectMinY2 );
2190     tools::Rectangle aLowerLeft ( aRectMinX1, aRectMaxY1, aRectMinX2, aRectMaxY2 );
2191     tools::Rectangle aUpperRight( aRectMaxX1, aRectMinY1, aRectMaxX2, aRectMinY2 );
2192 
2193     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerRight ) ), lclCornerRectTransparency );
2194     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperLeft  ) ), lclCornerRectTransparency );
2195     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerLeft  ) ), lclCornerRectTransparency );
2196     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperRight ) ), lclCornerRectTransparency );
2197 }
2198 
DrawOneChange(SCCOL nRefStartX,SCROW nRefStartY,SCCOL nRefEndX,SCROW nRefEndY,const Color & rColor,sal_uInt16 nType)2199 void ScOutputData::DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
2200                                 SCCOL nRefEndX, SCROW nRefEndY,
2201                                 const Color& rColor, sal_uInt16 nType )
2202 {
2203     PutInOrder( nRefStartX, nRefEndX );
2204     PutInOrder( nRefStartY, nRefEndY );
2205 
2206     if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
2207         mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, mnTab );
2208 
2209     if ( !(nRefStartX <= mnVisX2 + 1 && nRefEndX >= mnVisX1 &&
2210          nRefStartY <= mnVisY2 + 1 && nRefEndY >= mnVisY1) )       // +1 because it touches next cells left/top
2211         return;
2212 
2213     tools::Long nMinX = mnScrX;
2214     tools::Long nMinY = mnScrY;
2215     tools::Long nMaxX = mnScrX+mnScrW-1;
2216     tools::Long nMaxY = mnScrY+mnScrH-1;
2217     if ( mbLayoutRTL )
2218         std::swap( nMinX, nMaxX );
2219     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2220 
2221     bool bTop    = false;
2222     bool bBottom = false;
2223     bool bLeft   = false;
2224     bool bRight  = false;
2225 
2226     tools::Long nPosY = mnScrY;
2227     bool bNoStartY = ( mnY1 < nRefStartY );
2228     bool bNoEndY   = false;
2229     for (SCSIZE nArrY=1; nArrY<mnArrCount; nArrY++)      // loop to end for bNoEndY check
2230     {
2231         SCROW nY = mpRowInfo[nArrY].nRowNo;
2232 
2233         if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
2234         {
2235             nMinY = nPosY - 1;
2236             bTop = true;
2237         }
2238         if ( nY==nRefEndY )
2239         {
2240             nMaxY = nPosY + mpRowInfo[nArrY].nHeight - 1;
2241             bBottom = true;
2242         }
2243         if ( nY>nRefEndY && bNoEndY )
2244         {
2245             nMaxY = nPosY - 1;
2246             bBottom = true;
2247         }
2248         bNoStartY = ( nY < nRefStartY );
2249         bNoEndY   = ( nY < nRefEndY );
2250         nPosY += mpRowInfo[nArrY].nHeight;
2251     }
2252 
2253     tools::Long nPosX = mnScrX;
2254     if ( mbLayoutRTL )
2255         nPosX += mnMirrorW - 1;      // always in pixels
2256 
2257     for (SCCOL nX=mnX1; nX<=mnX2+1; nX++)
2258     {
2259         if ( nX==nRefStartX )
2260         {
2261             nMinX = nPosX - nLayoutSign;
2262             bLeft = true;
2263         }
2264         if ( nX==nRefEndX )
2265         {
2266             nMaxX = nPosX + ( mpRowInfo[0].basicCellInfo(nX).nWidth - 1 ) * nLayoutSign;
2267             bRight = true;
2268         }
2269         nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2270     }
2271 
2272     if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
2273         return;
2274 
2275     if ( nType == SC_CAT_DELETE_ROWS )
2276         bLeft = bRight = bBottom = false;       //! thick lines???
2277     else if ( nType == SC_CAT_DELETE_COLS )
2278         bTop = bBottom = bRight = false;        //! thick lines???
2279 
2280     mpDev->SetLineColor( rColor );
2281     if (bTop && bBottom && bLeft && bRight)
2282     {
2283         mpDev->SetFillColor();
2284         mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
2285     }
2286     else
2287     {
2288         if (bTop)
2289         {
2290             mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
2291             if ( nType == SC_CAT_DELETE_ROWS )
2292                 mpDev->DrawLine( Point( nMinX,nMinY+1 ), Point( nMaxX,nMinY+1 ) );
2293         }
2294         if (bBottom)
2295             mpDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
2296         if (bLeft)
2297         {
2298             mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
2299             if ( nType == SC_CAT_DELETE_COLS )
2300                 mpDev->DrawLine( Point( nMinX+nLayoutSign,nMinY ), Point( nMinX+nLayoutSign,nMaxY ) );
2301         }
2302         if (bRight)
2303             mpDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
2304     }
2305     if ( bLeft && bTop )
2306     {
2307         mpDev->SetLineColor();
2308         mpDev->SetFillColor( rColor );
2309         mpDev->DrawRect( tools::Rectangle( nMinX+nLayoutSign, nMinY+1, nMinX+3*nLayoutSign, nMinY+3 ) );
2310     }
2311 }
2312 
DrawChangeTrack()2313 void ScOutputData::DrawChangeTrack()
2314 {
2315     ScChangeTrack* pTrack = mpDoc->GetChangeTrack();
2316     ScChangeViewSettings* pSettings = mpDoc->GetChangeViewSettings();
2317     if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
2318         return;         // nothing there or hidden
2319 
2320     ScActionColorChanger aColorChanger(*pTrack);
2321 
2322     //  clipping happens from the outside
2323     //! without clipping, only paint affected cells ??!??!?
2324 
2325     SCCOL nEndX = mnX2;
2326     SCROW nEndY = mnY2;
2327     if ( nEndX < mpDoc->MaxCol() ) ++nEndX;      // also from the next cell since the mark
2328     if ( nEndY < mpDoc->MaxRow() ) ++nEndY;      // protrudes from the preceding cell
2329     ScRange aViewRange( mnX1, mnY1, mnTab, nEndX, nEndY, mnTab );
2330     const ScChangeAction* pAction = pTrack->GetFirst();
2331     while (pAction)
2332     {
2333         if ( pAction->IsVisible() )
2334         {
2335             ScChangeActionType eActionType = pAction->GetType();
2336             const ScBigRange& rBig = pAction->GetBigRange();
2337             if ( rBig.aStart.Tab() == mnTab )
2338             {
2339                 ScRange aRange = rBig.MakeRange( *mpDoc );
2340 
2341                 if ( eActionType == SC_CAT_DELETE_ROWS )
2342                     aRange.aEnd.SetRow( aRange.aStart.Row() );
2343                 else if ( eActionType == SC_CAT_DELETE_COLS )
2344                     aRange.aEnd.SetCol( aRange.aStart.Col() );
2345 
2346                 if ( aRange.Intersects( aViewRange ) &&
2347                      ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
2348                 {
2349                     aColorChanger.Update( *pAction );
2350                     Color aColor( aColorChanger.GetColor() );
2351                     DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
2352                                     aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
2353 
2354                 }
2355             }
2356             if ( eActionType == SC_CAT_MOVE &&
2357                     static_cast<const ScChangeActionMove*>(pAction)->
2358                         GetFromRange().aStart.Tab() == mnTab )
2359             {
2360                 ScRange aRange = static_cast<const ScChangeActionMove*>(pAction)->
2361                         GetFromRange().MakeRange( *mpDoc );
2362                 if ( aRange.Intersects( aViewRange ) &&
2363                      ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
2364                 {
2365                     aColorChanger.Update( *pAction );
2366                     Color aColor( aColorChanger.GetColor() );
2367                     DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
2368                                     aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
2369                 }
2370             }
2371         }
2372 
2373         pAction = pAction->GetNext();
2374     }
2375 }
2376 
DrawSparklines(vcl::RenderContext & rRenderContext)2377 void ScOutputData::DrawSparklines(vcl::RenderContext& rRenderContext)
2378 {
2379     tools::Long nInitPosX = mnScrX;
2380     if ( mbLayoutRTL )
2381         nInitPosX += mnMirrorW - 1;              // always in pixels
2382     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2383 
2384     tools::Long nPosY = mnScrY;
2385     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
2386     {
2387         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
2388         if ( pThisRowInfo->bChanged )
2389         {
2390             tools::Long nPosX = nInitPosX;
2391             for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2392             {
2393                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2394                 bool bIsMerged = false;
2395 
2396                 SCCOL nOverX = nX;
2397                 SCROW nOverY = pThisRowInfo->nRowNo;
2398                 tools::Long nStartPosX = nPosX;
2399                 tools::Long nStartPosY = nPosY;
2400 
2401                 ScAddress aCurrentAddress(nX, mpRowInfo[nArrY].nRowNo, mnTab);
2402                 std::shared_ptr<sc::Sparkline> pSparkline = mpDoc->GetSparkline(aCurrentAddress);
2403 
2404                 if (pInfo->bHOverlapped || pInfo->bVOverlapped)
2405                 {
2406                     while (nOverX > 0 && (mpDoc->GetAttr(
2407                            nOverX, nOverY, mnTab, ATTR_MERGE_FLAG)->GetValue() & ScMF::Hor))
2408                     {
2409                         --nOverX;
2410                         nStartPosX -= nLayoutSign
2411                                       * static_cast<tools::Long>(mpDoc->GetColWidth(nOverX, mnTab)
2412                                                                  * mnPPTX);
2413                     }
2414 
2415                     while (nOverY > 0 && (mpDoc->GetAttr(
2416                            nOverX, nOverY, mnTab, ATTR_MERGE_FLAG)->GetValue() & ScMF::Ver))
2417                     {
2418                         --nOverY;
2419                         nStartPosY -= nLayoutSign
2420                                       * static_cast<tools::Long>(mpDoc->GetRowHeight(nOverY, mnTab)
2421                                                                  * mnPPTY);
2422                     }
2423 
2424                     pSparkline = mpDoc->GetSparkline(ScAddress(nOverX, nOverY, mnTab));
2425                     bIsMerged = pSparkline ? true : false;
2426                 }
2427 
2428                 if (!mpDoc->ColHidden(nX, mnTab) && pSparkline
2429                     && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
2430                 {
2431                     tools::Long nWidth = mpRowInfo[0].basicCellInfo(nX).nWidth;
2432                     tools::Long nHeight = pThisRowInfo->nHeight;
2433 
2434                     if (bIsMerged || pInfo->bMerged)
2435                     {
2436                         const ScMergeAttr* pMerge = mpDoc->GetAttr(nOverX, nOverY, mnTab, ATTR_MERGE);
2437                         SCROW nCountX = pMerge->GetColMerge();
2438                         if (nCountX > 0)
2439                         {
2440                             sal_Int32 nIndex = 1;
2441                             while (nCountX > nIndex && (mpDoc->GetAttr(
2442                                    nOverX + nIndex, nOverY, mnTab, ATTR_MERGE_FLAG)->GetValue() & ScMF::Hor))
2443                             {
2444                                 nWidth += nLayoutSign
2445                                           * static_cast<tools::Long>(
2446                                               mpDoc->GetColWidth(nOverX + nIndex, mnTab) * mnPPTX);
2447                                 nIndex++;
2448                             }
2449                         }
2450 
2451                         SCROW nCountY = pMerge->GetRowMerge();
2452                         if (nCountY > 0)
2453                         {
2454                             sal_Int32 nIndex = 1;
2455                             while (nCountY > nIndex && (mpDoc->GetAttr(
2456                                    nOverX, nOverY + nIndex, mnTab, ATTR_MERGE_FLAG)->GetValue() & ScMF::Ver))
2457                             {
2458                                 nHeight += nLayoutSign
2459                                            * static_cast<tools::Long>(
2460                                                mpDoc->GetRowHeight(nOverY + nIndex, mnTab) * mnPPTY);
2461                                 nIndex++;
2462                             }
2463                         }
2464                     }
2465 
2466                     Point aPoint(nStartPosX, nStartPosY);
2467                     Size aSize(nWidth, nHeight);
2468 
2469                     sc::SparklineRenderer renderer(*mpDoc);
2470                     renderer.render(pSparkline, rRenderContext, tools::Rectangle(aPoint, aSize), 1, 1, double(maZoomX), double(maZoomY));
2471                 }
2472 
2473                 nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2474             }
2475         }
2476         nPosY += pThisRowInfo->nHeight;
2477     }
2478 
2479 }
2480 
2481 //TODO: moggi Need to check if this can't be written simpler
DrawNoteMarks(vcl::RenderContext & rRenderContext)2482 void ScOutputData::DrawNoteMarks(vcl::RenderContext& rRenderContext)
2483 {
2484     // cool#6911 draw the note indicator browser-side instead
2485     if (comphelper::LibreOfficeKit::isActive())
2486         return;
2487 
2488     tools::Long nInitPosX = mnScrX;
2489     if ( mbLayoutRTL )
2490         nInitPosX += mnMirrorW - 1;              // always in pixels
2491     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2492 
2493     tools::Long nPosY = mnScrY - 1;
2494     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
2495     {
2496         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
2497         if ( pThisRowInfo->bChanged )
2498         {
2499             tools::Long nPosX = nInitPosX;
2500             for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2501             {
2502                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2503                 bool bIsMerged = false;
2504 
2505                 if ( nX==mnX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
2506                 {
2507                     // find start of merged cell
2508                     bIsMerged = true;
2509                     SCROW nY = mpRowInfo[nArrY].nRowNo;
2510                     SCCOL nMergeX = nX;
2511                     SCROW nMergeY = nY;
2512                     mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, mnTab );
2513                 }
2514 
2515                 if (!mpDoc->ColHidden(nX, mnTab) && mpDoc->GetNote(nX, mpRowInfo[nArrY].nRowNo, mnTab)
2516                     && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
2517                 {
2518                     ScModule* mod = ScModule::get();
2519                     rRenderContext.SetLineColor(mod->GetColorConfig().GetColorValue(svtools::CALCGRID).nColor);
2520 
2521                     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2522                     if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
2523                         rRenderContext.SetFillColor( mod->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
2524                     else
2525                         rRenderContext.SetFillColor( mod->GetColorConfig().GetColorValue(svtools::CALCCOMMENTS).nColor );
2526 
2527                     tools::Long nMarkX = nPosX + ( mpRowInfo[0].basicCellInfo(nX).nWidth - 1) * nLayoutSign;
2528                     if ( bIsMerged || pInfo->bMerged )
2529                     {
2530                         //  if merged, add widths of all cells
2531                         SCCOL nNextX = nX + 1;
2532                         while ( nNextX <= mnX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
2533                         {
2534                             nMarkX += mpRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
2535                             ++nNextX;
2536                         }
2537                     }
2538                     // DPI/ZOOM 100/100 => 6, 100/50 => 4.5, 100/150 => 7.5
2539                     // DPI/ZOOM 150/100 => 7.5, 150/50 => 6, 150/150 => 9
2540                     sal_Int16 nSize = officecfg::Office::Calc::Content::Display::NoteIndicator::get();
2541                     if (nSize < 1)
2542                     {
2543                        const double fSize(rRenderContext.GetDPIScaleFactor() * maZoomX * 3 + 3);
2544                        nSize = static_cast<sal_Int16>(fSize);
2545                     }
2546                     Point aPoints[3];
2547                     aPoints[0] = Point(nMarkX, nPosY);
2548                     aPoints[0].setX( mbLayoutRTL ? aPoints[0].X() + nSize : aPoints[0].X() - nSize );
2549                     aPoints[1] = Point(nMarkX, nPosY);
2550                     aPoints[2] = Point(nMarkX, nPosY + nSize);
2551                     tools::Polygon aPoly(3, aPoints);
2552 
2553                     if ( mbLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < mnScrX+mnScrW ) )
2554                         rRenderContext.DrawPolygon(aPoly);
2555                 }
2556 
2557                 nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2558             }
2559         }
2560         nPosY += pThisRowInfo->nHeight;
2561     }
2562 }
2563 
DrawFormulaMarks(vcl::RenderContext & rRenderContext)2564 void ScOutputData::DrawFormulaMarks(vcl::RenderContext& rRenderContext)
2565 {
2566     bool bFirst = true;
2567 
2568     tools::Long nInitPosX = mnScrX;
2569     if ( mbLayoutRTL )
2570         nInitPosX += mnMirrorW - 1;              // always in pixels
2571     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2572 
2573     tools::Long nPosY = mnScrY;
2574     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
2575     {
2576         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
2577         if ( pThisRowInfo->bChanged )
2578         {
2579             tools::Long nPosX = nInitPosX;
2580             for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2581             {
2582                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2583                 if (!mpDoc->ColHidden(nX, mnTab) && !mpDoc->GetFormula(nX, mpRowInfo[nArrY].nRowNo, mnTab).isEmpty()
2584                     && (!pInfo->bHOverlapped && !pInfo->bVOverlapped))
2585                 {
2586                     if (bFirst)
2587                     {
2588                         rRenderContext.SetLineColor(COL_WHITE);
2589 
2590                         const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2591                         if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
2592                             rRenderContext.SetFillColor( ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
2593                         else
2594                             rRenderContext.SetFillColor(COL_LIGHTBLUE);
2595 
2596                         bFirst = false;
2597                     }
2598 
2599                     tools::Long nMarkX = nPosX;
2600                     tools::Long nMarkY = nPosY + pThisRowInfo->nHeight - 2;
2601                     if ( pInfo->bMerged )
2602                     {
2603                         for (SCSIZE nNextY=nArrY+1; nNextY+1<mnArrCount; nNextY++)
2604                         {
2605                             bool bVOver;
2606                             if (mpRowInfo[nNextY + 1].nRowNo == (mpRowInfo[nNextY].nRowNo + 1)) {
2607                                 bVOver = mpRowInfo[nNextY].cellInfo(nX).bVOverlapped;
2608                             } else {
2609                                 bVOver = mpDoc->GetAttr(nX,nNextY,mnTab,ATTR_MERGE_FLAG)->IsVerOverlapped();
2610                             }
2611                             if (!bVOver) break;
2612                             nMarkY += mpRowInfo[nNextY].nHeight;
2613                         }
2614                     }
2615                     // DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
2616                     // DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
2617                     const double nSize( rRenderContext.GetDPIScaleFactor() * maZoomX * 6 + 4);
2618                     Point aPoints[3];
2619                     aPoints[0] = Point(nMarkX, nMarkY);
2620                     aPoints[0].setX( mbLayoutRTL ? aPoints[0].X() - nSize : aPoints[0].X() + nSize );
2621                     aPoints[1] = Point(nMarkX, nMarkY);
2622                     aPoints[2] = Point(nMarkX, nMarkY - nSize);
2623                     tools::Polygon aPoly(3, aPoints);
2624 
2625                     if ( mbLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < mnScrX+mnScrW ) )
2626                         rRenderContext.DrawPolygon(aPoly);
2627                 }
2628 
2629                 nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2630             }
2631         }
2632         nPosY += pThisRowInfo->nHeight;
2633     }
2634 }
2635 
AddPDFNotes()2636 void ScOutputData::AddPDFNotes()
2637 {
2638     vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( mpDev->GetExtOutDevData() );
2639     if ( !pPDFData || !pPDFData->GetIsExportNotes() )
2640         return;
2641 
2642     tools::Long nInitPosX = mnScrX;
2643     if ( mbLayoutRTL )
2644     {
2645         Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
2646         tools::Long nOneX = aOnePixel.Width();
2647         nInitPosX += mnMirrorW - nOneX;
2648     }
2649     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2650 
2651     tools::Long nPosY = mnScrY;
2652     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
2653     {
2654         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
2655         if ( pThisRowInfo->bChanged )
2656         {
2657             tools::Long nPosX = nInitPosX;
2658             for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2659             {
2660                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2661                 bool bIsMerged = false;
2662                 SCROW nY = mpRowInfo[nArrY].nRowNo;
2663                 SCCOL nMergeX = nX;
2664                 SCROW nMergeY = nY;
2665 
2666                 if ( nX==mnX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
2667                 {
2668                     // find start of merged cell
2669                     bIsMerged = true;
2670                     mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, mnTab );
2671                     // use origin's pCell for NotePtr test below
2672                 }
2673 
2674                 ScPostIt* pNote = mpDoc->GetNote(nMergeX, nMergeY, mnTab);
2675 
2676                 if ( pNote && ( bIsMerged || ( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
2677                 {
2678                     tools::Long nNoteWidth = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
2679                     tools::Long nNoteHeight = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTY );
2680 
2681                     tools::Long nMarkX = nPosX + ( mpRowInfo[0].basicCellInfo(nX).nWidth - nNoteWidth ) * nLayoutSign;
2682                     if ( bIsMerged || pInfo->bMerged )
2683                     {
2684                         //  if merged, add widths of all cells
2685                         SCCOL nNextX = nX + 1;
2686                         while ( nNextX <= mnX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
2687                         {
2688                             nMarkX += mpRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
2689                             ++nNextX;
2690                         }
2691                     }
2692                     if ( mbLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < mnScrX+mnScrW ) )
2693                     {
2694                         tools::Rectangle aNoteRect( nMarkX, nPosY, nMarkX+nNoteWidth*nLayoutSign, nPosY+nNoteHeight );
2695 
2696                         vcl::pdf::PDFNote aNote;
2697 
2698                         // Note title is the cell address (as on printed note pages)
2699                         ScAddress aAddress( nMergeX, nMergeY, mnTab );
2700                         aNote.maTitle = aAddress.Format(ScRefFlags::VALID, mpDoc, mpDoc->GetAddressConvention());
2701 
2702                         // Content has to be a simple string without line breaks
2703                         OUString aContent = pNote->GetText();
2704                         aNote.maContents = aContent.replaceAll("\n", " ");
2705 
2706                         // If the caption is hidden, we need to show it to get its rectangle,
2707                         // then hide it again because it is also hidden in the file.
2708                         bool bShowCaption = pNote->IsCaptionShown();
2709                         if (!bShowCaption)
2710                             pNote->ShowCaption(aAddress, true);
2711 
2712                         SdrCaptionObj* pCaption = pNote->GetCaption();
2713                         tools::Rectangle aCaptionRect(pCaption->GetLogicRect());
2714                         Point aPoint(aCaptionRect.getX() + mnScrX, aCaptionRect.getY() + mnScrY);
2715                         Size aSize(aCaptionRect.GetWidth(), aCaptionRect.GetHeight());
2716                         tools::Rectangle aPopupRect(aPoint, aSize);
2717 
2718                         if (!bShowCaption)
2719                             pNote->ShowCaption(aAddress, false);
2720 
2721                         pPDFData->CreateNote(aNoteRect, aNote, aPopupRect);
2722                     }
2723                 }
2724 
2725                 nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2726             }
2727         }
2728         nPosY += pThisRowInfo->nHeight;
2729     }
2730 }
2731 
DrawClipMarks()2732 void ScOutputData::DrawClipMarks()
2733 {
2734     if (!mbAnyClipped)
2735         return;
2736 
2737     ScModule* mod = ScModule::get();
2738     Color aArrowFillCol(mod->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).nColor);
2739     const bool bIsDarkBackground = mod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
2740 
2741     DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
2742 
2743     tools::Long nInitPosX = mnScrX;
2744     if ( mbLayoutRTL )
2745         nInitPosX += mnMirrorW - 1;              // always in pixels
2746     tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1;
2747 
2748     tools::Rectangle aCellRect;
2749     tools::Long nPosY = mnScrY;
2750     for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++)
2751     {
2752         RowInfo* pThisRowInfo = &mpRowInfo[nArrY];
2753         if ( pThisRowInfo->bChanged )
2754         {
2755             SCROW nY = pThisRowInfo->nRowNo;
2756             tools::Long nPosX = nInitPosX;
2757             for (SCCOL nX=mnX1; nX<=mnX2; nX++)
2758             {
2759                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2760                 if (pInfo->nClipMark != ScClipMark::NONE)
2761                 {
2762                     if (pInfo->bHOverlapped || pInfo->bVOverlapped)
2763                     {
2764                         //  merge origin may be outside of visible area - use document functions
2765 
2766                         SCCOL nOverX = nX;
2767                         SCROW nOverY = nY;
2768                         tools::Long nStartPosX = nPosX;
2769                         tools::Long nStartPosY = nPosY;
2770 
2771                         while ( nOverX > 0 && ( mpDoc->GetAttr(
2772                                 nOverX, nOverY, mnTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Hor ) )
2773                         {
2774                             --nOverX;
2775                             nStartPosX -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,mnTab) * mnPPTX );
2776                         }
2777 
2778                         while ( nOverY > 0 && ( mpDoc->GetAttr(
2779                                 nOverX, nOverY, mnTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Ver ) )
2780                         {
2781                             --nOverY;
2782                             nStartPosY -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,mnTab) * mnPPTY );
2783                         }
2784 
2785                         tools::Long nOutWidth = static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,mnTab) * mnPPTX );
2786                         tools::Long nOutHeight = static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,mnTab) * mnPPTY );
2787 
2788                         const ScMergeAttr* pMerge = mpDoc->GetAttr( nOverX, nOverY, mnTab, ATTR_MERGE );
2789                         SCCOL nCountX = pMerge->GetColMerge();
2790                         for (SCCOL i=1; i<nCountX; i++)
2791                             nOutWidth += mpDoc->GetColWidth(nOverX+i,mnTab) * mnPPTX;
2792                         SCROW nCountY = pMerge->GetRowMerge();
2793                         nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, mnTab, mnPPTY);
2794 
2795                         if ( mbLayoutRTL )
2796                             nStartPosX -= nOutWidth - 1;
2797                         aCellRect = tools::Rectangle( Point( nStartPosX, nStartPosY ), Size( nOutWidth, nOutHeight ) );
2798                     }
2799                     else
2800                     {
2801                         tools::Long nOutWidth = mpRowInfo[0].basicCellInfo(nX).nWidth;
2802                         tools::Long nOutHeight = pThisRowInfo->nHeight;
2803 
2804                         if ( pInfo->bMerged && pInfo->pPatternAttr )
2805                         {
2806                             SCCOL nOverX = nX;
2807                             SCROW nOverY = nY;
2808                             const ScMergeAttr* pMerge =
2809                                     &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
2810                             SCCOL nCountX = pMerge->GetColMerge();
2811                             for (SCCOL i=1; i<nCountX; i++)
2812                                 nOutWidth += mpDoc->GetColWidth(nOverX+i,mnTab) * mnPPTX;
2813                             SCROW nCountY = pMerge->GetRowMerge();
2814                             nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, mnTab, mnPPTY);
2815                         }
2816 
2817                         tools::Long nStartPosX = nPosX;
2818                         if ( mbLayoutRTL )
2819                             nStartPosX -= nOutWidth - 1;
2820                         // #i80447# create aCellRect from two points in case nOutWidth is 0
2821                         aCellRect = tools::Rectangle( Point( nStartPosX, nPosY ),
2822                                                Point( nStartPosX+nOutWidth-1, nPosY+nOutHeight-1 ) );
2823                     }
2824 
2825                     aCellRect.AdjustBottom( -1 );    // don't paint over the cell grid
2826                     if ( mbLayoutRTL )
2827                         aCellRect.AdjustLeft(1 );
2828                     else
2829                         aCellRect.AdjustRight( -1 );
2830 
2831                     tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
2832                     Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );
2833 
2834                     const Color aColor = pInfo->maBackground ?
2835                         static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem())->GetColor() :
2836                         COL_AUTO;
2837                     if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
2838                         mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::WhiteLine );
2839                     else
2840                         mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::BlackLine );
2841 
2842                     if (mbVertical)
2843                     {
2844                         if (pInfo->nClipMark & (mbLayoutRTL ? ScClipMark::Bottom : ScClipMark::Top))
2845                         {
2846                             //  visually top
2847                             tools::Rectangle aMarkRect = aCellRect;
2848                             aMarkRect.SetBottom(aCellRect.Top() + nMarkPixel - 1);
2849                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true, true);
2850                         }
2851                         if (pInfo->nClipMark & (mbLayoutRTL ? ScClipMark::Top : ScClipMark::Bottom))
2852                         {
2853                             //  visually bottom
2854                             tools::Rectangle aMarkRect = aCellRect;
2855                             aMarkRect.SetTop(aCellRect.Bottom() + nMarkPixel + 1);
2856                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
2857                                 true);
2858                         }
2859                     }
2860                     else
2861                     {
2862                         if (pInfo->nClipMark & (mbLayoutRTL ? ScClipMark::Right : ScClipMark::Left))
2863                         {
2864                             //  visually left
2865                             tools::Rectangle aMarkRect = aCellRect;
2866                             aMarkRect.SetRight(aCellRect.Left() + nMarkPixel - 1);
2867                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true,
2868                                 false);
2869                         }
2870                         if (pInfo->nClipMark & (mbLayoutRTL ? ScClipMark::Left : ScClipMark::Right))
2871                         {
2872                             //  visually right
2873                             tools::Rectangle aMarkRect = aCellRect;
2874                             aMarkRect.SetLeft(aCellRect.Right() - nMarkPixel + 1);
2875                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
2876                                 false);
2877                         }
2878                     }
2879                 }
2880                 nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2881             }
2882         }
2883         nPosY += pThisRowInfo->nHeight;
2884     }
2885 
2886     mpDev->SetDrawMode(nOldDrawMode);
2887 }
2888 
2889 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2890