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