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