calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / sc / source / ui / view / gridwin4.cxx
blob390c4b2bdb637259fb0624a2e3acf658dc5da235
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <scitems.hxx>
22 #include <editeng/eeitem.hxx>
24 #include <svtools/colorcfg.hxx>
25 #include <editeng/colritem.hxx>
26 #include <editeng/editview.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/brushitem.hxx>
29 #include <sfx2/bindings.hxx>
30 #include <sfx2/printer.hxx>
31 #include <vcl/cursor.hxx>
32 #include <vcl/settings.hxx>
33 #include <o3tl/unit_conversion.hxx>
34 #include <osl/diagnose.h>
36 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
37 #include <comphelper/lok.hxx>
38 #include <comphelper/scopeguard.hxx>
39 #include <sfx2/lokhelper.hxx>
40 #include <sfx2/lokcomponenthelpers.hxx>
42 #include <svx/svdview.hxx>
43 #include <svx/svdpagv.hxx>
44 #include <svx/sdrpagewindow.hxx>
45 #include <svx/sdr/contact/objectcontactofpageview.hxx>
46 #include <svx/sdr/contact/viewobjectcontact.hxx>
47 #include <svx/sdr/contact/viewcontact.hxx>
48 #include <tabvwsh.hxx>
49 #include <vcl/lineinfo.hxx>
50 #include <vcl/sysdata.hxx>
52 #include <gridwin.hxx>
53 #include <viewdata.hxx>
54 #include <output.hxx>
55 #include <document.hxx>
56 #include <attrib.hxx>
57 #include <patattr.hxx>
58 #include <dbdata.hxx>
59 #include <docoptio.hxx>
60 #include <notemark.hxx>
61 #include <dbfunc.hxx>
62 #include <scmod.hxx>
63 #include <inputhdl.hxx>
64 #include <rfindlst.hxx>
65 #include <hiranges.hxx>
66 #include <pagedata.hxx>
67 #include <docpool.hxx>
68 #include <globstr.hrc>
69 #include <scresid.hxx>
70 #include <docsh.hxx>
71 #include <cbutton.hxx>
72 #include <invmerge.hxx>
73 #include <editutil.hxx>
74 #include <inputopt.hxx>
75 #include <fillinfo.hxx>
76 #include <dpcontrol.hxx>
77 #include <queryparam.hxx>
78 #include <queryentry.hxx>
79 #include <markdata.hxx>
80 #include <sc.hrc>
81 #include <vcl/virdev.hxx>
82 #include <svx/sdrpaintwindow.hxx>
83 #include <drwlayer.hxx>
85 static void lcl_LimitRect( tools::Rectangle& rRect, const tools::Rectangle& rVisible )
87 if ( rRect.Top() < rVisible.Top()-1 ) rRect.SetTop( rVisible.Top()-1 );
88 if ( rRect.Bottom() > rVisible.Bottom()+1 ) rRect.SetBottom( rVisible.Bottom()+1 );
90 // The header row must be drawn also when the inner rectangle is not visible,
91 // that is why there is no return value anymore.
92 // When it is far away, then lcl_DrawOneFrame is not even called.
95 static void lcl_DrawOneFrame( vcl::RenderContext* pDev, const tools::Rectangle& rInnerPixel,
96 const OUString& rTitle, const Color& rColor, bool bTextBelow,
97 double nPPTX, double nPPTY, const Fraction& rZoomY,
98 ScDocument& rDoc, ScViewData& rButtonViewData, bool bLayoutRTL )
100 // rButtonViewData is only used to set the button size,
102 tools::Rectangle aInner = rInnerPixel;
103 if ( bLayoutRTL )
105 aInner.SetLeft( rInnerPixel.Right() );
106 aInner.SetRight( rInnerPixel.Left() );
109 tools::Rectangle aVisible( Point(0,0), pDev->GetOutputSizePixel() );
110 lcl_LimitRect( aInner, aVisible );
112 tools::Rectangle aOuter = aInner;
113 tools::Long nHor = static_cast<tools::Long>( SC_SCENARIO_HSPACE * nPPTX );
114 tools::Long nVer = static_cast<tools::Long>( SC_SCENARIO_VSPACE * nPPTY );
115 aOuter.AdjustLeft( -nHor );
116 aOuter.AdjustRight(nHor );
117 aOuter.AdjustTop( -nVer );
118 aOuter.AdjustBottom(nVer );
120 // use ScPatternAttr::GetFont only for font size
121 vcl::Font aAttrFont;
122 rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN).
123 fillFontOnly(aAttrFont, pDev, &rZoomY);
125 // everything else from application font
126 vcl::Font aAppFont = pDev->GetSettings().GetStyleSettings().GetAppFont();
127 aAppFont.SetFontSize( aAttrFont.GetFontSize() );
129 aAppFont.SetAlignment( ALIGN_TOP );
130 pDev->SetFont( aAppFont );
132 Size aTextSize( pDev->GetTextWidth( rTitle ), pDev->GetTextHeight() );
134 if ( bTextBelow )
135 aOuter.AdjustBottom(aTextSize.Height() );
136 else
137 aOuter.AdjustTop( -(aTextSize.Height()) );
139 pDev->SetLineColor();
140 pDev->SetFillColor( rColor );
141 // left, top, right, bottom
142 pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aInner.Left(), aOuter.Bottom() ) );
143 pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aOuter.Right(), aInner.Top() ) );
144 pDev->DrawRect( tools::Rectangle( aInner.Right(), aOuter.Top(), aOuter.Right(), aOuter.Bottom() ) );
145 pDev->DrawRect( tools::Rectangle( aOuter.Left(), aInner.Bottom(), aOuter.Right(), aOuter.Bottom() ) );
147 tools::Long nButtonY = bTextBelow ? aInner.Bottom() : aOuter.Top();
149 ScDDComboBoxButton aComboButton(pDev);
150 aComboButton.SetOptSizePixel();
151 tools::Long nBWidth = tools::Long(aComboButton.GetSizePixel().Width() * rZoomY);
152 tools::Long nBHeight = nVer + aTextSize.Height() + 1;
153 Size aButSize( nBWidth, nBHeight );
154 tools::Long nButtonPos = bLayoutRTL ? aOuter.Left() : aOuter.Right()-nBWidth+1;
155 aComboButton.Draw( Point(nButtonPos, nButtonY), aButSize );
156 rButtonViewData.SetScenButSize( aButSize );
158 tools::Long nTextStart = bLayoutRTL ? aInner.Right() - aTextSize.Width() + 1 : aInner.Left();
160 bool bWasClip = false;
161 vcl::Region aOldClip;
162 bool bClip = ( aTextSize.Width() > aOuter.Right() - nBWidth - aInner.Left() );
163 if ( bClip )
165 if (pDev->IsClipRegion())
167 bWasClip = true;
168 aOldClip = pDev->GetActiveClipRegion();
170 tools::Long nClipStartX = bLayoutRTL ? aOuter.Left() + nBWidth : aInner.Left();
171 tools::Long nClipEndX = bLayoutRTL ? aInner.Right() : aOuter.Right() - nBWidth;
172 pDev->SetClipRegion( vcl::Region(tools::Rectangle( nClipStartX, nButtonY + nVer/2,
173 nClipEndX, nButtonY + nVer/2 + aTextSize.Height())) );
176 pDev->DrawText( Point( nTextStart, nButtonY + nVer/2 ), rTitle );
178 if ( bClip )
180 if ( bWasClip )
181 pDev->SetClipRegion(aOldClip);
182 else
183 pDev->SetClipRegion();
186 pDev->SetFillColor();
187 pDev->SetLineColor( COL_BLACK );
188 pDev->DrawRect( aInner );
189 pDev->DrawRect( aOuter );
192 static void lcl_DrawScenarioFrames( OutputDevice* pDev, ScViewData& rViewData, ScSplitPos eWhich,
193 SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
195 ScDocument& rDoc = rViewData.GetDocument();
196 SCTAB nTab = rViewData.GetTabNo();
197 SCTAB nTabCount = rDoc.GetTableCount();
198 if ( nTab+1 >= nTabCount || !rDoc.IsScenario(nTab+1) || rDoc.IsScenario(nTab) )
199 return;
201 if ( nX1 > 0 ) --nX1;
202 if ( nY1>=2 ) nY1 -= 2; // Hack: Header row affects two cells
203 else if ( nY1 > 0 ) --nY1;
204 if ( nX2 < rDoc.MaxCol() ) ++nX2;
205 if ( nY2 < rDoc.MaxRow()-1 ) nY2 += 2; // Hack: Header row affects two cells
206 else if ( nY2 < rDoc.MaxRow() ) ++nY2;
207 ScRange aViewRange( nX1,nY1,nTab, nX2,nY2,nTab );
209 //! cache the ranges in table!!!!
211 ScMarkData aMarks(rDoc.GetSheetLimits());
212 for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
213 rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
214 ScRangeListRef xRanges = new ScRangeList;
215 aMarks.FillRangeListWithMarks( xRanges.get(), false );
217 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
218 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
220 for (size_t j = 0, n = xRanges->size(); j < n; ++j)
222 ScRange aRange = (*xRanges)[j];
223 // Always extend scenario frame to merged cells where no new non-covered cells
224 // are framed
225 rDoc.ExtendTotalMerge( aRange );
227 //! -> Extend repaint when merging !!!
229 if ( aRange.Intersects( aViewRange ) ) //! Space for Text/Button?
231 Point aStartPos = rViewData.GetScrPos(
232 aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
233 Point aEndPos = rViewData.GetScrPos(
234 aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich, true );
235 // on the grid:
236 aStartPos.AdjustX( -nLayoutSign );
237 aStartPos.AdjustY( -1 );
238 aEndPos.AdjustX( -nLayoutSign );
239 aEndPos.AdjustY( -1 );
241 bool bTextBelow = ( aRange.aStart.Row() == 0 );
243 OUString aCurrent;
244 Color aColor( COL_LIGHTGRAY );
245 for (SCTAB nAct=nTab+1; nAct<nTabCount && rDoc.IsScenario(nAct); nAct++)
246 if ( rDoc.IsActiveScenario(nAct) && rDoc.HasScenarioRange(nAct,aRange) )
248 OUString aDummyComment;
249 ScScenarioFlags nDummyFlags;
250 rDoc.GetName( nAct, aCurrent );
251 rDoc.GetScenarioData( nAct, aDummyComment, aColor, nDummyFlags );
254 if (aCurrent.isEmpty())
255 aCurrent = ScResId( STR_EMPTYDATA );
257 //! Own text "(None)" instead of "(Empty)" ???
259 lcl_DrawOneFrame( pDev, tools::Rectangle( aStartPos, aEndPos ),
260 aCurrent, aColor, bTextBelow,
261 rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomY(),
262 rDoc, rViewData, bLayoutRTL );
267 static void lcl_DrawHighlight( ScOutputData& rOutputData, const ScViewData& rViewData,
268 const std::vector<ScHighlightEntry>& rHighlightRanges )
270 SCTAB nTab = rViewData.GetTabNo();
271 for ( const auto& rHighlightRange : rHighlightRanges)
273 ScRange aRange = rHighlightRange.aRef;
274 if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
276 rOutputData.DrawRefMark(
277 aRange.aStart.Col(), aRange.aStart.Row(),
278 aRange.aEnd.Col(), aRange.aEnd.Row(),
279 rHighlightRange.aColor, false );
284 // Calculates top-left offset to be applied based on margins and indent.
285 static void lcl_GetEditAreaTLOffset(tools::Long& nOffsetX, tools::Long& nOffsetY, const ScAddress& rAddr,
286 const ScViewData& rViewData, ScDocument& rDoc)
288 tools::Long nLeftMargin = 0;
289 tools::Long nTopMargin = 0;
290 tools::Long nIndent = 0;
291 tools::Long nDummy = 0;
292 ScEditUtil aEUtil(&rDoc, rAddr.Col(), rAddr.Row(), rAddr.Tab(),
293 Point(0, 0), nullptr, rViewData.GetPPTX(),
294 rViewData.GetPPTY(), Fraction(1.0), Fraction(1.0),
295 false /* bPrintTwips */);
296 const ScPatternAttr* pPattern = rDoc.GetPattern(rAddr);
297 if (!rDoc.IsLayoutRTL(rAddr.Tab()))
298 nIndent = aEUtil.GetIndent(pPattern);
299 aEUtil.GetMargins(pPattern, nLeftMargin, nTopMargin, nDummy, nDummy);
300 nOffsetX = nIndent + nLeftMargin;
301 nOffsetY = nTopMargin;
304 void ScGridWindow::DoInvertRect( const tools::Rectangle& rPixel )
306 if ( rPixel == aInvertRect )
307 aInvertRect = tools::Rectangle(); // Cancel
308 else
310 OSL_ENSURE( aInvertRect.IsEmpty(), "DoInvertRect no pairs" );
312 aInvertRect = rPixel; // Mark new rectangle
315 UpdateHeaderOverlay(); // uses aInvertRect
318 void ScGridWindow::PrePaint(vcl::RenderContext& /*rRenderContext*/)
320 // forward PrePaint to DrawingLayer
321 ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
323 if(pTabViewShell)
325 SdrView* pDrawView = pTabViewShell->GetScDrawView();
327 if (pDrawView)
329 pDrawView->PrePaint();
334 bool ScGridWindow::NeedLOKCursorInvalidation(const tools::Rectangle& rCursorRect,
335 const Fraction aScaleX, const Fraction aScaleY)
337 // Don't see the need for a map as there will be only a few zoom levels
338 // and as of now X and Y zooms in online are the same.
339 for (auto& rEntry : maLOKLastCursor)
341 if (aScaleX == rEntry.aScaleX && aScaleY == rEntry.aScaleY)
343 if (rCursorRect == rEntry.aRect)
344 return false; // No change
346 // Update and allow invalidate.
347 rEntry.aRect = rCursorRect;
348 return true;
352 maLOKLastCursor.push_back(LOKCursorEntry{aScaleX, aScaleY, rCursorRect});
353 return true;
356 void ScGridWindow::InvalidateLOKViewCursor(const tools::Rectangle& rCursorRect,
357 const Fraction aScaleX, const Fraction aScaleY)
359 if (!NeedLOKCursorInvalidation(rCursorRect, aScaleX, aScaleY))
360 return;
362 ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
363 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
365 while (pViewShell)
367 if (pViewShell != pThisViewShell && pViewShell->GetDocId() == pThisViewShell->GetDocId())
369 ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
370 if (pOtherViewShell)
372 ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
373 Fraction aZoomX = rOtherViewData.GetZoomX();
374 Fraction aZoomY = rOtherViewData.GetZoomY();
375 if (aZoomX == aScaleX && aZoomY == aScaleY)
377 SfxLokHelper::notifyOtherView(pThisViewShell, pOtherViewShell,
378 LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", rCursorRect.toString());
383 pViewShell = SfxViewShell::GetNext(*pViewShell);
387 void ScGridWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect )
389 ScDocument& rDoc = mrViewData.GetDocument();
390 if ( rDoc.IsInInterpreter() )
392 // Via Reschedule, interpreted cells do not trigger Invalidate again,
393 // otherwise for instance an error box would never appear (bug 36381).
394 // Later, through bNeedsRepaint everything is painted again.
395 if ( bNeedsRepaint )
397 //! Merge Rectangle?
398 aRepaintPixel = tools::Rectangle(); // multiple -> paint all
400 else
402 bNeedsRepaint = true;
403 aRepaintPixel = LogicToPixel(rRect); // only affected ranges
405 return;
408 // #i117893# If GetSizePixel needs to call the resize handler, the resulting nested Paint call
409 // (possibly for a larger rectangle) has to be allowed. Call GetSizePixel before setting bIsInPaint.
410 GetSizePixel();
412 if (bIsInPaint)
413 return;
415 bIsInPaint = true;
417 tools::Rectangle aPixRect = LogicToPixel( rRect );
419 SCCOL nX1 = mrViewData.GetPosX(eHWhich);
420 SCROW nY1 = mrViewData.GetPosY(eVWhich);
422 SCTAB nTab = mrViewData.GetTabNo();
424 double nPPTX = mrViewData.GetPPTX();
425 double nPPTY = mrViewData.GetPPTY();
427 tools::Rectangle aMirroredPixel = aPixRect;
428 if ( rDoc.IsLayoutRTL( nTab ) )
430 // mirror and swap
431 tools::Long nWidth = GetSizePixel().Width();
432 aMirroredPixel.SetLeft( nWidth - 1 - aPixRect.Right() );
433 aMirroredPixel.SetRight( nWidth - 1 - aPixRect.Left() );
436 tools::Long nScrX = ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
437 while ( nScrX <= aMirroredPixel.Left() && nX1 < rDoc.MaxCol() )
439 ++nX1;
440 nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
442 SCCOL nX2 = nX1;
443 while ( nScrX <= aMirroredPixel.Right() && nX2 < rDoc.MaxCol() )
445 ++nX2;
446 nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX2, nTab ), nPPTX );
449 tools::Long nScrY = 0;
450 ScViewData::AddPixelsWhile( nScrY, aPixRect.Top(), nY1, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
451 SCROW nY2 = nY1;
452 if (nScrY <= aPixRect.Bottom() && nY2 < rDoc.MaxRow())
454 ++nY2;
455 ScViewData::AddPixelsWhile( nScrY, aPixRect.Bottom(), nY2, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
458 Draw( nX1,nY1,nX2,nY2, ScUpdateMode::Marks ); // don't continue with painting
460 bIsInPaint = false;
463 void ScGridWindow::Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, ScUpdateMode eMode )
465 ScDocument& rDoc = mrViewData.GetDocument();
467 // let's ignore the normal Draw() attempts when doing the tiled rendering,
468 // all the rendering should go through PaintTile() in that case.
469 // TODO revisit if we can actually turn this into an assert(), and clean
470 // up the callers
471 if (comphelper::LibreOfficeKit::isActive())
472 return;
474 ScModule* pScMod = SC_MOD();
475 bool bTextWysiwyg = pScMod->GetInputOptions().GetTextWysiwyg();
477 if (mrViewData.IsMinimized())
478 return;
480 PutInOrder( nX1, nX2 );
481 PutInOrder( nY1, nY2 );
483 OSL_ENSURE( rDoc.ValidCol(nX2) && rDoc.ValidRow(nY2), "GridWin Draw area too big" );
485 UpdateVisibleRange();
487 if (nX2 < maVisibleRange.mnCol1 || nY2 < maVisibleRange.mnRow1)
488 return;
489 // invisible
490 if (nX1 < maVisibleRange.mnCol1)
491 nX1 = maVisibleRange.mnCol1;
492 if (nY1 < maVisibleRange.mnRow1)
493 nY1 = maVisibleRange.mnRow1;
495 if (nX1 > maVisibleRange.mnCol2 || nY1 > maVisibleRange.mnRow2)
496 return;
498 if (nX2 > maVisibleRange.mnCol2)
499 nX2 = maVisibleRange.mnCol2;
500 if (nY2 > maVisibleRange.mnRow2)
501 nY2 = maVisibleRange.mnRow2;
503 if ( eMode != ScUpdateMode::Marks && nX2 < maVisibleRange.mnCol2)
504 nX2 = maVisibleRange.mnCol2; // to continue painting
506 // point of no return
508 ++nPaintCount; // mark that painting is in progress
510 SCTAB nTab = mrViewData.GetTabNo();
511 rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
513 Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
514 tools::Long nMirrorWidth = GetSizePixel().Width();
515 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
516 if ( bLayoutRTL )
518 tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, maVisibleRange.mnRow1, eWhich ).X();
519 nMirrorWidth = aScrPos.X() - nEndPixel;
520 aScrPos.setX( nEndPixel + 1 );
523 tools::Long nScrX = aScrPos.X();
524 tools::Long nScrY = aScrPos.Y();
526 SCCOL nCurX = mrViewData.GetCurX();
527 SCROW nCurY = mrViewData.GetCurY();
528 SCCOL nCurEndX = nCurX;
529 SCROW nCurEndY = nCurY;
530 rDoc.ExtendMerge( nCurX, nCurY, nCurEndX, nCurEndY, nTab );
531 bool bCurVis = nCursorHideCount==0 &&
532 ( nCurEndX+1 >= nX1 && nCurX <= nX2+1 && nCurEndY+1 >= nY1 && nCurY <= nY2+1 );
534 // AutoFill Handles
535 if ( !bCurVis && nCursorHideCount==0 && bAutoMarkVisible && aAutoMarkPos.Tab() == nTab &&
536 ( aAutoMarkPos.Col() != nCurX || aAutoMarkPos.Row() != nCurY ) )
538 SCCOL nHdlX = aAutoMarkPos.Col();
539 SCROW nHdlY = aAutoMarkPos.Row();
540 rDoc.ExtendMerge( nHdlX, nHdlY, nHdlX, nHdlY, nTab );
541 // left and top is unaffected
543 //! Paint AutoFill handles alone (without Cursor) ???
546 double nPPTX = mrViewData.GetPPTX();
547 double nPPTY = mrViewData.GetPPTY();
549 const ScViewOptions& rOpts = mrViewData.GetOptions();
551 // data block
553 ScTableInfo aTabInfo;
554 rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
555 nPPTX, nPPTY, false, rOpts.GetOption(VOPT_FORMULAS),
556 &mrViewData.GetMarkData() );
558 Fraction aZoomX = mrViewData.GetZoomX();
559 Fraction aZoomY = mrViewData.GetZoomY();
560 ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
561 nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
562 &aZoomX, &aZoomY );
564 aOutputData.SetMirrorWidth( nMirrorWidth ); // needed for RTL
565 aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
567 ScopedVclPtr< VirtualDevice > xFmtVirtDev;
568 bool bLogicText = bTextWysiwyg; // call DrawStrings in logic MapMode?
570 if ( bTextWysiwyg )
572 // use printer for text formatting
574 OutputDevice* pFmtDev = rDoc.GetPrinter();
575 pFmtDev->SetMapMode( mrViewData.GetLogicMode(eWhich) );
576 aOutputData.SetFmtDevice( pFmtDev );
578 else if ( aZoomX != aZoomY && mrViewData.IsOle() )
580 // #i45033# For OLE inplace editing with different zoom factors,
581 // use a virtual device with 1/100th mm as text formatting reference
583 xFmtVirtDev.disposeAndReset( VclPtr<VirtualDevice>::Create() );
584 xFmtVirtDev->SetMapMode(MapMode(MapUnit::Map100thMM));
585 aOutputData.SetFmtDevice( xFmtVirtDev.get() );
587 bLogicText = true; // use logic MapMode
590 DrawContent(*GetOutDev(), aTabInfo, aOutputData, bLogicText);
592 // If something was inverted during the Paint (selection changed from Basic Macro)
593 // then this is now mixed up and has to be repainted
594 OSL_ENSURE(nPaintCount, "Wrong nPaintCount");
595 --nPaintCount;
596 if (!nPaintCount)
597 CheckNeedsRepaint();
599 // Flag drawn formula cells "unchanged".
600 rDoc.ResetChanged(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
601 rDoc.PrepareFormulaCalc();
604 namespace {
606 class SuppressEditViewMessagesGuard
608 public:
609 SuppressEditViewMessagesGuard(EditView& rEditView) :
610 mrEditView(rEditView),
611 mbOrigSuppressFlag(rEditView.IsSuppressLOKMessages())
613 if (!mbOrigSuppressFlag)
614 mrEditView.SuppressLOKMessages(true);
617 ~SuppressEditViewMessagesGuard()
619 if (mrEditView.IsSuppressLOKMessages() != mbOrigSuppressFlag)
620 mrEditView.SuppressLOKMessages(mbOrigSuppressFlag);
623 private:
624 EditView& mrEditView;
625 const bool mbOrigSuppressFlag;
631 * Used to store the necessary information about the (combined-)tile
632 * area relevant to coordinate transformations in RTL mode.
634 class ScLokRTLContext
636 public:
637 ScLokRTLContext(const ScOutputData& rOutputData, const tools::Long nTileDeviceOriginPixelX):
638 mrOutputData(rOutputData),
639 mnTileDevOriginX(nTileDeviceOriginPixelX)
643 * Converts from document x pixel position to the
644 * corresponding pixel position w.r.t the tile device origin.
646 tools::Long docToTilePos(tools::Long nPosX) const
648 tools::Long nMirrorX = (-2 * mnTileDevOriginX) + mrOutputData.GetScrW();
649 return nMirrorX - 1 - nPosX;
653 private:
654 const ScOutputData& mrOutputData;
655 const tools::Long mnTileDevOriginX;
658 namespace
660 int lcl_GetMultiLineHeight(EditEngine* pEditEngine)
662 int nHeight = 0;
663 int nParagraphs = pEditEngine->GetParagraphCount();
664 if (nParagraphs > 1 || (nParagraphs > 0 && pEditEngine->GetLineCount(0) > 1))
666 for (int nPara = 0; nPara < nParagraphs; nPara++)
668 nHeight += pEditEngine->GetLineCount(nPara) * pEditEngine->GetLineHeight(nPara);
672 return nHeight;
675 tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
677 return tools::Rectangle(-rRect.Right(), rRect.Top(), -rRect.Left(), rRect.Bottom());
681 void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData,
682 bool bLogicText)
684 ScModule* pScMod = SC_MOD();
685 ScDocument& rDoc = mrViewData.GetDocument();
686 const ScViewOptions& rOpts = mrViewData.GetOptions();
687 bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
688 bool bNoBackgroundAndGrid = bIsTiledRendering
689 && comphelper::LibreOfficeKit::isCompatFlagSet(
690 comphelper::LibreOfficeKit::Compat::scNoGridBackground);
692 SCTAB nTab = aOutputData.nTab;
693 SCCOL nX1 = aOutputData.nX1;
694 SCROW nY1 = aOutputData.nY1;
695 SCCOL nX2 = aOutputData.nX2;
696 SCROW nY2 = aOutputData.nY2;
697 tools::Long nScrX = aOutputData.nScrX;
698 tools::Long nScrY = aOutputData.nScrY;
700 const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig();
701 Color aGridColor( rColorCfg.GetColorValue( svtools::CALCGRID ).nColor );
702 if ( aGridColor == COL_TRANSPARENT )
704 // use view options' grid color only if color config has "automatic" color
705 aGridColor = rOpts.GetGridColor();
708 aOutputData.SetSyntaxMode ( mrViewData.IsSyntaxMode() );
709 aOutputData.SetGridColor ( aGridColor );
710 aOutputData.SetShowNullValues ( rOpts.GetOption( VOPT_NULLVALS ) );
711 aOutputData.SetShowFormulas ( rOpts.GetOption( VOPT_FORMULAS ) );
712 aOutputData.SetShowSpellErrors ( rDoc.GetDocOptions().IsAutoSpell() );
713 aOutputData.SetMarkClipped ( rOpts.GetOption( VOPT_CLIPMARKS ) );
715 aOutputData.SetUseStyleColor( true ); // always set in table view
717 aOutputData.SetViewShell( mrViewData.GetViewShell() );
719 bool bGrid = rOpts.GetOption( VOPT_GRID ) && mrViewData.GetShowGrid();
720 bool bGridFirst = !rOpts.GetOption( VOPT_GRID_ONTOP );
722 bool bPage = rOpts.GetOption( VOPT_PAGEBREAKS ) && !bIsTiledRendering;
724 bool bPageMode = mrViewData.IsPagebreakMode();
725 if (bPageMode) // after FindChanged
727 // SetPagebreakMode also initializes bPrinted Flags
728 aOutputData.SetPagebreakMode( mrViewData.GetView()->GetPageBreakData() );
731 EditView* pEditView = nullptr;
732 bool bEditMode = mrViewData.HasEditView(eWhich);
733 if ( bEditMode && mrViewData.GetRefTabNo() == nTab )
735 SCCOL nEditCol;
736 SCROW nEditRow;
737 mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
738 SCCOL nEditEndCol = mrViewData.GetEditEndCol();
739 SCROW nEditEndRow = mrViewData.GetEditEndRow();
741 if ( nEditEndCol >= nX1 && nEditCol <= nX2 && nEditEndRow >= nY1 && nEditRow <= nY2 )
742 aOutputData.SetEditCell( nEditCol, nEditRow );
743 else
744 bEditMode = false;
747 const MapMode aOriginalMode = rDevice.GetMapMode();
749 // define drawing layer map mode and paint rectangle
750 MapMode aDrawMode = GetDrawMapMode();
751 if (bIsTiledRendering)
753 // FIXME this shouldn't be necessary once we change the entire Calc to
754 // work in the logic coordinates (ideally 100ths of mm - so that it is
755 // the same as editeng and drawinglayer), and get rid of all the
756 // SetMapMode's and other unnecessary fun we have with pixels
757 // See also ScGridWindow::GetDrawMapMode() for the rest of this hack
758 aDrawMode.SetOrigin(PixelToLogic(Point(nScrX, nScrY), aDrawMode));
760 tools::Rectangle aDrawingRectLogic;
761 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
762 bool bLokRTL = bLayoutRTL && bIsTiledRendering;
763 std::unique_ptr<ScLokRTLContext> pLokRTLCtxt(
764 bLokRTL ?
765 new ScLokRTLContext(aOutputData, o3tl::convert(aOriginalMode.GetOrigin().X(), o3tl::Length::twip, o3tl::Length::px)) :
766 nullptr);
769 // get drawing pixel rect
770 tools::Rectangle aDrawingRectPixel(
771 bLokRTL ? Point(-(nScrX + aOutputData.GetScrW()), nScrY) : Point(nScrX, nScrY),
772 Size(aOutputData.GetScrW(), aOutputData.GetScrH()));
774 // correct for border (left/right)
775 if(rDoc.MaxCol() == nX2 && !bLokRTL)
777 if(bLayoutRTL)
779 aDrawingRectPixel.SetLeft( 0 );
781 else
783 aDrawingRectPixel.SetRight( GetOutputSizePixel().getWidth() );
787 // correct for border (bottom)
788 if(rDoc.MaxRow() == nY2)
790 aDrawingRectPixel.SetBottom( GetOutputSizePixel().getHeight() );
793 // get logic positions
794 aDrawingRectLogic = PixelToLogic(aDrawingRectPixel, aDrawMode);
797 bool bInPlaceEditing = bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo());
798 vcl::Cursor* pInPlaceCrsr = nullptr;
799 bool bInPlaceVisCursor = false;
800 if (bInPlaceEditing)
802 // toggle the cursor off if it's on to ensure the cursor invert
803 // background logic remains valid after the background is cleared on
804 // the next cursor flash
805 pInPlaceCrsr = pEditView->GetCursor();
806 bInPlaceVisCursor = pInPlaceCrsr && pInPlaceCrsr->IsVisible();
807 if (bInPlaceVisCursor)
808 pInPlaceCrsr->Hide();
811 OutputDevice* pContentDev = &rDevice; // device for document content, used by overlay manager
812 SdrPaintWindow* pTargetPaintWindow = nullptr; // #i74769# work with SdrPaintWindow directly
815 // init redraw
816 ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
818 if(pTabViewShell)
820 MapMode aCurrentMapMode(pContentDev->GetMapMode());
821 pContentDev->SetMapMode(aDrawMode);
822 SdrView* pDrawView = pTabViewShell->GetScDrawView();
824 if(pDrawView)
826 // #i74769# Use new BeginDrawLayers() interface
827 vcl::Region aDrawingRegion(aDrawingRectLogic);
828 pTargetPaintWindow = pDrawView->BeginDrawLayers(pContentDev, aDrawingRegion);
829 OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");
831 if (!bIsTiledRendering)
833 // #i74769# get target device from SdrPaintWindow, this may be the prerender
834 // device now, too.
835 pContentDev = &(pTargetPaintWindow->GetTargetOutputDevice());
836 aOutputData.SetContentDevice(pContentDev);
840 pContentDev->SetMapMode(aCurrentMapMode);
844 // app-background / document edge (area) (Pixel)
845 if ( !bIsTiledRendering && ( nX2 == rDoc.MaxCol() || nY2 == rDoc.MaxRow() ) )
847 // save MapMode and set to pixel
848 MapMode aCurrentMapMode(pContentDev->GetMapMode());
849 pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
851 tools::Rectangle aPixRect( Point(), GetOutputSizePixel() );
852 pContentDev->SetFillColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor );
853 pContentDev->SetLineColor();
854 if ( nX2==rDoc.MaxCol() )
856 tools::Rectangle aDrawRect( aPixRect );
857 if ( bLayoutRTL )
858 aDrawRect.SetRight( nScrX - 1 );
859 else
860 aDrawRect.SetLeft( nScrX + aOutputData.GetScrW() );
861 if (aDrawRect.Right() >= aDrawRect.Left())
862 pContentDev->DrawRect( aDrawRect );
864 if ( nY2==rDoc.MaxRow() )
866 tools::Rectangle aDrawRect( aPixRect );
867 aDrawRect.SetTop( nScrY + aOutputData.GetScrH() );
868 if ( nX2==rDoc.MaxCol() )
870 // no double painting of the corner
871 if ( bLayoutRTL )
872 aDrawRect.SetLeft( nScrX );
873 else
874 aDrawRect.SetRight( nScrX + aOutputData.GetScrW() - 1 );
876 if (aDrawRect.Bottom() >= aDrawRect.Top())
877 pContentDev->DrawRect( aDrawRect );
880 // restore MapMode
881 pContentDev->SetMapMode(aCurrentMapMode);
884 if ( rDoc.HasBackgroundDraw( nTab, aDrawingRectLogic ) )
886 pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
887 aOutputData.DrawClear();
889 // drawing background
891 pContentDev->SetMapMode(aDrawMode);
892 DrawRedraw( aOutputData, SC_LAYER_BACK );
894 else
895 aOutputData.SetSolidBackground(!bNoBackgroundAndGrid);
897 aOutputData.DrawDocumentBackground();
899 if (bGridFirst && (bGrid || bPage))
901 // Draw lines in background color cover over lok client grid lines in merged cell areas if bNoBackgroundAndGrid is set.
902 if (bNoBackgroundAndGrid)
903 aOutputData.DrawGrid(*pContentDev, false /* bGrid */, false /* bPage */, true /* bMergeCover */);
904 else
905 aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
908 aOutputData.DrawBackground(*pContentDev);
910 if (!bGridFirst && (bGrid || bPage) && !bNoBackgroundAndGrid)
911 aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
913 pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
915 //tdf#128258 - draw a dotted line before hidden columns/rows
916 DrawHiddenIndicator(nX1,nY1,nX2,nY2, *pContentDev);
918 if ( bPageMode )
920 // DrawPagePreview draws complete lines/page numbers, must always be clipped
921 if ( aOutputData.SetChangedClip() )
923 DrawPagePreview(nX1,nY1,nX2,nY2, *pContentDev);
924 pContentDev->SetClipRegion();
928 aOutputData.DrawShadow();
929 aOutputData.DrawFrame(*pContentDev);
931 aOutputData.DrawSparklines(*pContentDev);
933 // Show Note Mark
934 if ( rOpts.GetOption( VOPT_NOTES ) )
935 aOutputData.DrawNoteMarks(*pContentDev);
937 if ( !bLogicText )
938 aOutputData.DrawStrings(); // in pixel MapMode
940 // edit cells and printer-metrics text must be before the buttons
941 // (DataPilot buttons contain labels in UI font)
943 pContentDev->SetMapMode(mrViewData.GetLogicMode(eWhich));
944 if ( bLogicText )
945 aOutputData.DrawStrings(true); // in logic MapMode if bLogicText is set
946 aOutputData.DrawEdit(true);
948 // the buttons are painted in absolute coordinates
949 if (bIsTiledRendering)
951 // Tiled offset nScrX, nScrY
952 MapMode aMap( MapUnit::MapPixel );
953 Point aOrigin = aOriginalMode.GetOrigin();
954 aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px) + nScrX);
955 aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px) + nScrY);
956 aMap.SetOrigin(aOrigin);
957 pContentDev->SetMapMode(aMap);
959 else
960 pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
962 // Autofilter- and Pivot-Buttons
963 DrawButtons(nX1, nX2, rTableInfo, pContentDev, pLokRTLCtxt.get()); // Pixel
965 pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
967 aOutputData.DrawClipMarks();
969 // In any case, Scenario / ChangeTracking must happen after DrawGrid, also for !bGridFirst
971 //! test if ChangeTrack display is active
972 //! Disable scenario frame via view option?
974 SCTAB nTabCount = rDoc.GetTableCount();
975 const std::vector<ScHighlightEntry> &rHigh = mrViewData.GetView()->GetHighlightRanges();
976 bool bHasScenario = ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) );
977 bool bHasChange = ( rDoc.GetChangeTrack() != nullptr );
979 if ( bHasChange || bHasScenario || !rHigh.empty() )
981 //! Merge SetChangedClip() with DrawMarks() ?? (different MapMode!)
983 if ( bHasChange )
984 aOutputData.DrawChangeTrack();
986 if ( bHasScenario )
987 lcl_DrawScenarioFrames( pContentDev, mrViewData, eWhich, nX1,nY1,nX2,nY2 );
989 lcl_DrawHighlight( aOutputData, mrViewData, rHigh );
992 // Drawing foreground
994 pContentDev->SetMapMode(aDrawMode);
996 // Bitmaps and buttons are in absolute pixel coordinates.
997 const MapMode aOrig = pContentDev->GetMapMode();
998 if (bIsTiledRendering)
1000 Point aOrigin = aOriginalMode.GetOrigin();
1001 tools::Long nXOffset = bLayoutRTL ?
1002 (-o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px) + aOutputData.GetScrW()) :
1003 o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px);
1004 Size aPixelOffset(nXOffset, o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px));
1005 pContentDev->SetPixelOffset(aPixelOffset);
1006 comphelper::LibreOfficeKit::setLocalRendering();
1009 DrawRedraw( aOutputData, SC_LAYER_FRONT );
1010 DrawRedraw( aOutputData, SC_LAYER_INTERN );
1011 DrawSdrGrid( aDrawingRectLogic, pContentDev );
1013 if (bIsTiledRendering)
1015 pContentDev->SetPixelOffset(Size());
1016 pContentDev->SetMapMode(aOrig);
1019 pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
1021 if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() )
1023 Color aRefColor( rColorCfg.GetColorValue(svtools::CALCREFERENCE).nColor );
1024 aOutputData.DrawRefMark( mrViewData.GetRefStartX(), mrViewData.GetRefStartY(),
1025 mrViewData.GetRefEndX(), mrViewData.GetRefEndY(),
1026 aRefColor, false );
1029 // range finder
1031 ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
1032 if (pHdl)
1034 ScDocShell* pDocSh = mrViewData.GetDocShell();
1035 ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
1036 if ( pRangeFinder && !pRangeFinder->IsHidden() &&
1037 pRangeFinder->GetDocName() == pDocSh->GetTitle() )
1039 sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
1040 for (sal_uInt16 i=0; i<nCount; i++)
1042 ScRangeFindData& rData = pRangeFinder->GetObject(i);
1044 ScRange aRef = rData.aRef;
1045 aRef.PutInOrder();
1046 if ( aRef.aStart.Tab() >= nTab && aRef.aEnd.Tab() <= nTab )
1047 aOutputData.DrawRefMark( aRef.aStart.Col(), aRef.aStart.Row(),
1048 aRef.aEnd.Col(), aRef.aEnd.Row(),
1049 rData.nColor,
1050 true );
1056 // end redraw
1057 ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
1059 if(pTabViewShell)
1061 MapMode aCurrentMapMode(pContentDev->GetMapMode());
1062 pContentDev->SetMapMode(aDrawMode);
1064 if (bIsTiledRendering)
1066 Point aOrigin = aOriginalMode.GetOrigin();
1067 if (bLayoutRTL)
1068 aOrigin.setX(-o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
1069 + aOutputData.nScrX + aOutputData.GetScrW());
1070 else
1071 aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
1072 + aOutputData.nScrX);
1074 aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
1075 + aOutputData.nScrY);
1076 const double twipFactor = 15 * 1.76388889; // 26.45833335
1077 aOrigin = Point(aOrigin.getX() * twipFactor,
1078 aOrigin.getY() * twipFactor);
1079 MapMode aNew = rDevice.GetMapMode();
1080 aNew.SetOrigin(aOrigin);
1081 rDevice.SetMapMode(aNew);
1084 SdrView* pDrawView = pTabViewShell->GetScDrawView();
1086 if(pDrawView)
1088 // #i74769# work with SdrPaintWindow directly
1089 pDrawView->EndDrawLayers(*pTargetPaintWindow, true);
1092 pContentDev->SetMapMode(aCurrentMapMode);
1096 // paint in-place editing
1097 if (bIsTiledRendering)
1099 ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
1100 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1102 while (pViewShell)
1104 bool bEnterLoop = bIsTiledRendering || pViewShell != pThisViewShell;
1105 if (bEnterLoop && pViewShell->GetDocId() == pThisViewShell->GetDocId())
1107 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1108 if (pTabViewShell)
1110 ScViewData& rOtherViewData = pTabViewShell->GetViewData();
1111 ScSplitPos eOtherWhich = rOtherViewData.GetEditActivePart();
1113 bool bOtherEditMode = rOtherViewData.HasEditView(eOtherWhich);
1114 SCCOL nCol1 = rOtherViewData.GetEditStartCol();
1115 SCROW nRow1 = rOtherViewData.GetEditStartRow();
1116 SCCOL nCol2 = rOtherViewData.GetEditEndCol();
1117 SCROW nRow2 = rOtherViewData.GetEditEndRow();
1118 bOtherEditMode = bOtherEditMode
1119 && ( nCol2 >= nX1 && nCol1 <= nX2 && nRow2 >= nY1 && nRow1 <= nY2 );
1120 if (bOtherEditMode && rOtherViewData.GetRefTabNo() == nTab)
1122 EditView* pOtherEditView = rOtherViewData.GetEditView(eOtherWhich);
1123 if (pOtherEditView)
1125 tools::Long nScreenX = aOutputData.nScrX;
1126 tools::Long nScreenY = aOutputData.nScrY;
1128 rDevice.SetLineColor();
1129 SfxViewShell* pSfxViewShell = SfxViewShell::Current();
1130 ScTabViewShell* pCurrentViewShell = dynamic_cast<ScTabViewShell*>(pSfxViewShell);
1131 if (pCurrentViewShell)
1133 const ScViewData& pViewData = pCurrentViewShell->GetViewData();
1134 const ScViewOptions& aViewOptions = pViewData.GetOptions();
1135 const ScPatternAttr* pPattern = rDoc.GetPattern( nCol1, nRow1, nTab );
1136 Color aCellColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
1137 if (aCellColor.IsTransparent())
1139 aCellColor = aViewOptions.GetDocColor();
1141 rDevice.SetFillColor(aCellColor);
1142 pOtherEditView->SetBackgroundColor(aCellColor);
1144 Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eOtherWhich );
1145 Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eOtherWhich );
1147 if (bIsTiledRendering)
1149 EditEngine* pEditEngine = pOtherEditView->GetEditEngine();
1150 if (pEditEngine)
1151 aEnd.AdjustY(lcl_GetMultiLineHeight(pEditEngine));
1154 if (bLokRTL)
1156 // Transform the cell range X coordinates such that the edit cell area is
1157 // horizontally mirrored w.r.t the (combined-)tile.
1158 aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
1159 aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
1162 // don't overwrite grid
1163 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1164 aEnd.AdjustX( -(2 * nLayoutSign) );
1165 aEnd.AdjustY( -2 );
1167 tools::Rectangle aBackground(aStart, aEnd);
1168 if (bLokRTL)
1169 aBackground.Normalize();
1171 // Need to draw the background in absolute coords.
1172 Point aOrigin = aOriginalMode.GetOrigin();
1173 aOrigin.setX(
1174 o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
1175 + nScreenX);
1176 aOrigin.setY(
1177 o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
1178 + nScreenY);
1179 aBackground += aOrigin;
1180 rDevice.SetMapMode(aDrawMode);
1182 static const double twipFactor = 15 * 1.76388889; // 26.45833335
1183 // keep into account the zoom factor
1184 aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()),
1185 (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY()));
1187 MapMode aNew = rDevice.GetMapMode();
1188 aNew.SetOrigin(aOrigin);
1189 rDevice.SetMapMode(aNew);
1191 // paint the background
1192 rDevice.DrawRect(rDevice.PixelToLogic(aBackground));
1193 tools::Rectangle aBGAbs(aBackground);
1195 tools::Rectangle aEditRect(aBackground);
1196 tools::Long nOffsetX = 0, nOffsetY = 0;
1197 // Get top-left offset because of margin and indent.
1198 lcl_GetEditAreaTLOffset(nOffsetX, nOffsetY, ScAddress(nCol1, nRow1, nTab), mrViewData, rDoc);
1199 aEditRect.AdjustLeft(nOffsetX + 1);
1200 aEditRect.AdjustRight(1);
1201 aEditRect.AdjustTop(nOffsetY + 1);
1202 aEditRect.AdjustBottom(1);
1204 // EditView has an 'output area' which is used to clip the 'paint area' we provide below.
1205 // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
1206 // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
1207 // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
1208 OutputDevice& rOtherWin = pOtherEditView->GetOutputDevice();
1209 const tools::Rectangle aOrigOutputArea(pOtherEditView->GetOutputArea()); // Not in pixels.
1210 const MapMode aOrigMapMode = rOtherWin.GetMapMode();
1211 rOtherWin.SetMapMode(rDevice.GetMapMode());
1213 // Avoid sending wrong cursor/selection messages by the 'other' view, as the output-area is going
1214 // to be tweaked temporarily to match the current view's zoom.
1215 SuppressEditViewMessagesGuard aGuard(*pOtherEditView);
1216 comphelper::ScopeGuard aOutputGuard(
1217 [pOtherEditView, aOrigOutputArea, bLokRTL] {
1218 if (bLokRTL && aOrigOutputArea != pOtherEditView->GetOutputArea())
1219 pOtherEditView->SetOutputArea(aOrigOutputArea);
1222 aEditRect = rDevice.PixelToLogic(aEditRect);
1223 if (bIsTiledRendering)
1224 pOtherEditView->SetOutputArea(aEditRect);
1225 else
1226 aEditRect.Intersection(pOtherEditView->GetOutputArea());
1227 pOtherEditView->Paint(aEditRect, &rDevice);
1229 // EditView will do the cursor notifications correctly if we're in
1230 // print-twips messaging mode.
1231 if (bIsTiledRendering && !comphelper::LibreOfficeKit::isCompatFlagSet(
1232 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
1234 // Now we need to get relative cursor position within the editview.
1235 // This is for sending the pixel-aligned twips position of the cursor to the specific views with
1236 // the same given zoom level.
1237 tools::Rectangle aCursorRect = pOtherEditView->GetEditCursor();
1238 Point aCursPos = OutputDevice::LogicToLogic(aCursorRect.TopLeft(),
1239 MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
1241 const MapMode& rDevMM = rDevice.GetMapMode();
1242 MapMode aMM(MapUnit::MapTwip);
1243 aMM.SetScaleX(rDevMM.GetScaleX());
1244 aMM.SetScaleY(rDevMM.GetScaleY());
1246 aBGAbs.AdjustLeft(1);
1247 aBGAbs.AdjustTop(1);
1248 aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
1249 aCursorRect.setWidth(0);
1250 aCursorRect.Move(aCursPos.getX(), 0);
1251 // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
1252 InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
1255 // Rollback the mapmode and 'output area'.
1256 rOtherWin.SetMapMode(aOrigMapMode);
1257 if (!bIsTiledRendering)
1258 pOtherEditView->SetOutputArea(aOrigOutputArea);
1259 rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
1265 pViewShell = SfxViewShell::GetNext(*pViewShell);
1270 // In-place editing - when the user is typing, we need to paint the text
1271 // using the editeng.
1272 // It's being done after EndDrawLayers() to get it outside the overlay
1273 // buffer and on top of everything.
1274 if (bInPlaceEditing)
1276 // get the coordinates of the area we need to clear (overpaint by
1277 // the background)
1278 SCCOL nCol1 = mrViewData.GetEditStartCol();
1279 SCROW nRow1 = mrViewData.GetEditStartRow();
1280 SCCOL nCol2 = mrViewData.GetEditEndCol();
1281 SCROW nRow2 = mrViewData.GetEditEndRow();
1282 rDevice.SetLineColor();
1283 rDevice.SetFillColor(pEditView->GetBackgroundColor());
1284 Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eWhich );
1285 Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eWhich );
1287 if (bLokRTL)
1289 // Transform the cell range X coordinates such that the edit cell area is
1290 // horizontally mirrored w.r.t the (combined-)tile.
1291 aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
1292 aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
1295 // don't overwrite grid
1296 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1297 aEnd.AdjustX( -(2 * nLayoutSign) );
1298 aEnd.AdjustY( -2 );
1300 // set the correct mapmode
1301 tools::Rectangle aBackground(aStart, aEnd);
1302 if (bLokRTL)
1303 aBackground.Normalize();
1304 tools::Rectangle aBGAbs(aBackground);
1306 if (bIsTiledRendering)
1308 // Need to draw the background in absolute coords.
1309 Point aOrigin = aOriginalMode.GetOrigin();
1310 aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
1311 + nScrX);
1312 aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
1313 + nScrY);
1314 aBackground += aOrigin;
1315 rDevice.SetMapMode(aDrawMode);
1317 else
1318 rDevice.SetMapMode(mrViewData.GetLogicMode());
1320 if (bIsTiledRendering)
1322 Point aOrigin = aOriginalMode.GetOrigin();
1323 aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
1324 + nScrX);
1325 aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
1326 + nScrY);
1327 static const double twipFactor = 15 * 1.76388889; // 26.45833335
1328 // keep into account the zoom factor
1329 aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()),
1330 (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY()));
1331 MapMode aNew = rDevice.GetMapMode();
1332 aNew.SetOrigin(aOrigin);
1333 rDevice.SetMapMode(aNew);
1336 // paint the editeng text
1337 if (bIsTiledRendering)
1339 // EditView has an 'output area' which is used to clip the paint area we provide below.
1340 // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
1341 // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
1342 // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
1343 const MapMode aOrigMapMode = GetMapMode();
1344 SetMapMode(rDevice.GetMapMode());
1346 // Avoid sending wrong cursor/selection messages by the current view, as the output-area is going
1347 // to be tweaked temporarily to match other view's zoom. (This does not affect the manual
1348 // cursor-messaging done in the non print-twips mode)
1349 SuppressEditViewMessagesGuard aGuard(*pEditView);
1351 // EditView will do the cursor notifications correctly if we're in
1352 // print-twips messaging mode.
1353 if (!comphelper::LibreOfficeKit::isCompatFlagSet(
1354 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
1356 // Now we need to get relative cursor position within the editview.
1357 // This is for sending the pixel-aligned twips position of the cursor to the specific views with
1358 // the same given zoom level.
1359 tools::Rectangle aCursorRect = pEditView->GetEditCursor();
1360 Point aCursPos = o3tl::toTwips(aCursorRect.TopLeft(), o3tl::Length::mm100);
1362 const MapMode& rDevMM = rDevice.GetMapMode();
1363 MapMode aMM(MapUnit::MapTwip);
1364 aMM.SetScaleX(rDevMM.GetScaleX());
1365 aMM.SetScaleY(rDevMM.GetScaleY());
1367 aBGAbs.AdjustLeft(1);
1368 aBGAbs.AdjustTop(1);
1369 aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
1370 aCursorRect.setWidth(0);
1371 aCursorRect.Move(aCursPos.getX(), 0);
1372 // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
1373 InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
1376 // Rollback the mapmode and 'output area'.
1377 SetMapMode(aOrigMapMode);
1379 else
1381 // paint the background
1382 tools::Rectangle aLogicRect(rDevice.PixelToLogic(aBackground));
1383 //tdf#100925, rhbz#1283420, Draw some text here, to get
1384 //X11CairoTextRender::getCairoContext called, so that the forced read
1385 //from the underlying X Drawable gets it to sync.
1386 rDevice.DrawText(aLogicRect.BottomLeft(), " ");
1387 rDevice.DrawRect(aLogicRect);
1389 tools::Rectangle aEditRect(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH()));
1390 pEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice);
1393 rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
1395 // restore the cursor it was originally visible
1396 if (bInPlaceVisCursor)
1397 pInPlaceCrsr->Show();
1400 if (mrViewData.HasEditView(eWhich))
1402 // flush OverlayManager before changing the MapMode
1403 flushOverlayManager();
1405 // set MapMode for text edit
1406 rDevice.SetMapMode(mrViewData.GetLogicMode());
1408 else
1409 rDevice.SetMapMode(aDrawMode);
1411 if (mpNoteMarker)
1412 mpNoteMarker->Draw(); // Above the cursor, in drawing map mode
1414 if (bPage && bInitialPageBreaks)
1415 SetupInitialPageBreaks(rDoc, nTab);
1419 void ScGridWindow::SetupInitialPageBreaks(const ScDocument& rDoc, SCTAB nTab)
1421 // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page breaks
1422 // is enabled, breaks should be visible. If the document is opened the first
1423 // time, the breaks are not calculated yet, so for this initialization
1424 // a timer will be triggered here.
1425 std::set<SCCOL> aColBreaks;
1426 std::set<SCROW> aRowBreaks;
1427 rDoc.GetAllColBreaks(aColBreaks, nTab, true, false);
1428 rDoc.GetAllRowBreaks(aRowBreaks, nTab, true, false);
1429 if (aColBreaks.size() == 0 || aRowBreaks.size() == 0)
1431 maShowPageBreaksTimer.SetPriority(TaskPriority::DEFAULT_IDLE);
1432 maShowPageBreaksTimer.Start();
1434 bInitialPageBreaks = false;
1437 namespace
1439 template<typename IndexType>
1440 void lcl_getBoundingRowColumnforTile(ScViewData& rViewData,
1441 tools::Long nTileStartPosPx, tools::Long nTileEndPosPx,
1442 sal_Int32& nTopLeftTileOffset, sal_Int32& nTopLeftTileOrigin,
1443 sal_Int32& nTopLeftTileIndex, sal_Int32& nBottomRightTileIndex)
1445 const bool bColumnHeader = std::is_same<IndexType, SCCOL>::value;
1447 SCTAB nTab = rViewData.GetTabNo();
1449 IndexType nStartIndex = -1;
1450 IndexType nEndIndex = -1;
1451 tools::Long nStartPosPx = 0;
1452 tools::Long nEndPosPx = 0;
1454 ScPositionHelper& rPositionHelper =
1455 bColumnHeader ? rViewData.GetLOKWidthHelper() : rViewData.GetLOKHeightHelper();
1456 const auto& rStartNearest = rPositionHelper.getNearestByPosition(nTileStartPosPx);
1457 const auto& rEndNearest = rPositionHelper.getNearestByPosition(nTileEndPosPx);
1459 ScBoundsProvider aBoundsProvider(rViewData, nTab, bColumnHeader);
1460 aBoundsProvider.Compute(rStartNearest, rEndNearest, nTileStartPosPx, nTileEndPosPx);
1461 aBoundsProvider.GetStartIndexAndPosition(nStartIndex, nStartPosPx); ++nStartIndex;
1462 aBoundsProvider.GetEndIndexAndPosition(nEndIndex, nEndPosPx);
1464 nTopLeftTileOffset = nTileStartPosPx - nStartPosPx;
1465 nTopLeftTileOrigin = nStartPosPx;
1466 nTopLeftTileIndex = nStartIndex;
1467 nBottomRightTileIndex = nEndIndex;
1470 void lcl_RTLAdjustTileColOffset(ScViewData& rViewData, sal_Int32& nTileColOffset,
1471 tools::Long nTileEndPx, sal_Int32 nEndCol, SCTAB nTab,
1472 const ScDocument& rDoc, double fPPTX)
1474 auto GetColWidthPx = [&rDoc, nTab, fPPTX](SCCOL nCol) {
1475 const sal_uInt16 nSize = rDoc.GetColWidth(nCol, nTab);
1476 const tools::Long nSizePx = ScViewData::ToPixel(nSize, fPPTX);
1477 return nSizePx;
1480 ScPositionHelper rHelper = rViewData.GetLOKWidthHelper();
1481 tools::Long nEndColPos = rHelper.computePosition(nEndCol, GetColWidthPx);
1483 nTileColOffset += (nEndColPos - nTileEndPx - nTileColOffset);
1486 class ScLOKProxyObjectContact final : public sdr::contact::ObjectContactOfPageView
1488 private:
1489 ScDrawView* mpScDrawView;
1491 public:
1492 explicit ScLOKProxyObjectContact(
1493 ScDrawView* pDrawView,
1494 SdrPageWindow& rPageWindow,
1495 const char* pDebugName) :
1496 ObjectContactOfPageView(rPageWindow, pDebugName),
1497 mpScDrawView(pDrawView)
1501 virtual bool supportsGridOffsets() const override { return true; }
1503 virtual void calculateGridOffsetForViewOjectContact(
1504 basegfx::B2DVector& rTarget,
1505 const sdr::contact::ViewObjectContact& rClient) const override
1507 if (!mpScDrawView)
1508 return;
1510 SdrPageView* pPageView(mpScDrawView->GetSdrPageView());
1511 if (!pPageView)
1512 return;
1514 SdrPageWindow* pSdrPageWindow = nullptr;
1515 if (pPageView->PageWindowCount() > 0)
1516 pSdrPageWindow = pPageView->GetPageWindow(0);
1517 if (!pSdrPageWindow)
1518 return;
1520 sdr::contact::ObjectContact& rObjContact(pSdrPageWindow->GetObjectContact());
1522 SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
1523 if (pTargetSdrObject)
1524 rTarget = pTargetSdrObject->GetViewContact().GetViewObjectContact(rObjContact).getGridOffset();
1528 class ScLOKDrawView : public FmFormView
1530 public:
1531 ScLOKDrawView(OutputDevice* pOut, ScViewData& rData) :
1532 FmFormView(*rData.GetDocument().GetDrawLayer(), pOut),
1533 mpScDrawView(rData.GetScDrawView())
1537 virtual sdr::contact::ObjectContact* createViewSpecificObjectContact(
1538 SdrPageWindow& rPageWindow, const char* pDebugName) const override
1540 if (!mpScDrawView)
1541 return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName);
1543 return new ScLOKProxyObjectContact(mpScDrawView, rPageWindow, pDebugName);
1546 private:
1547 ScDrawView* mpScDrawView;
1549 } // anonymous namespace
1551 void ScGridWindow::PaintTile( VirtualDevice& rDevice,
1552 int nOutputWidth, int nOutputHeight,
1553 int nTilePosX, int nTilePosY,
1554 tools::Long nTileWidth, tools::Long nTileHeight,
1555 SCCOL nTiledRenderingAreaEndCol, SCROW nTiledRenderingAreaEndRow )
1557 Fraction origZoomX = mrViewData.GetZoomX();
1558 Fraction origZoomY = mrViewData.GetZoomY();
1560 // Output size is in pixels while tile position and size are in logical units (twips).
1562 // Assumption: always paint the whole sheet i.e. "visible" range is always
1563 // from (0,0) to last data position.
1565 // Tile geometry is independent of the zoom level, but the output size is
1566 // dependent of the zoom level. Determine the correct zoom level before
1567 // we start.
1569 // FIXME the painting works using a mixture of drawing with coordinates in
1570 // pixels and in logic coordinates; it should be cleaned up to use logic
1571 // coords only, and avoid all the SetMapMode()'s.
1572 // Similarly to Writer, we should set the mapmode once on the rDevice, and
1573 // not care about any zoom settings.
1575 Fraction aFracX(o3tl::convert(nOutputWidth, o3tl::Length::px, o3tl::Length::twip), nTileWidth);
1576 Fraction aFracY(o3tl::convert(nOutputHeight, o3tl::Length::px, o3tl::Length::twip), nTileHeight);
1578 const bool bChangeZoom = (aFracX != origZoomX || aFracY != origZoomY);
1580 // page break zoom, and aLogicMode in ScViewData
1581 // FIXME: there are issues when SetZoom is called conditionally.
1582 mrViewData.SetZoom(aFracX, aFracY, true);
1583 if (bChangeZoom)
1585 if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
1586 pDrawView->resetGridOffsetsForAllSdrPageViews();
1589 const double fTilePosXPixel = static_cast<double>(nTilePosX) * nOutputWidth / nTileWidth;
1590 const double fTilePosYPixel = static_cast<double>(nTilePosY) * nOutputHeight / nTileHeight;
1591 const double fTileBottomPixel = static_cast<double>(nTilePosY + nTileHeight) * nOutputHeight / nTileHeight;
1592 const double fTileRightPixel = static_cast<double>(nTilePosX + nTileWidth) * nOutputWidth / nTileWidth;
1594 SCTAB nTab = mrViewData.GetTabNo();
1595 ScDocument& rDoc = mrViewData.GetDocument();
1597 const double fPPTX = mrViewData.GetPPTX();
1598 const double fPPTY = mrViewData.GetPPTY();
1600 // find approximate col/row offsets of nearby.
1601 sal_Int32 nTopLeftTileRowOffset = 0;
1602 sal_Int32 nTopLeftTileColOffset = 0;
1603 sal_Int32 nTopLeftTileRowOrigin = 0;
1604 sal_Int32 nTopLeftTileColOrigin = 0;
1606 sal_Int32 nTopLeftTileRow = 0;
1607 sal_Int32 nTopLeftTileCol = 0;
1608 sal_Int32 nBottomRightTileRow = 0;
1609 sal_Int32 nBottomRightTileCol = 0;
1611 lcl_getBoundingRowColumnforTile<SCROW>(mrViewData,
1612 fTilePosYPixel, fTileBottomPixel,
1613 nTopLeftTileRowOffset, nTopLeftTileRowOrigin,
1614 nTopLeftTileRow, nBottomRightTileRow);
1616 lcl_getBoundingRowColumnforTile<SCCOL>(mrViewData,
1617 fTilePosXPixel, fTileRightPixel,
1618 nTopLeftTileColOffset, nTopLeftTileColOrigin,
1619 nTopLeftTileCol, nBottomRightTileCol);
1621 // Enlarge
1622 nBottomRightTileCol++;
1623 nBottomRightTileRow++;
1625 if (nBottomRightTileCol > rDoc.MaxCol())
1626 nBottomRightTileCol = rDoc.MaxCol();
1628 if (nBottomRightTileRow > MAXTILEDROW)
1629 nBottomRightTileRow = MAXTILEDROW;
1631 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1633 if (bLayoutRTL)
1635 lcl_RTLAdjustTileColOffset(mrViewData, nTopLeftTileColOffset,
1636 fTileRightPixel, nBottomRightTileCol, nTab,
1637 rDoc, fPPTX);
1640 // size of the document including drawings, charts, etc.
1641 SCCOL nEndCol = nTiledRenderingAreaEndCol;
1642 SCROW nEndRow = nTiledRenderingAreaEndRow;
1644 if (nEndCol < nBottomRightTileCol)
1645 nEndCol = nBottomRightTileCol;
1647 if (nEndRow < nBottomRightTileRow)
1648 nEndRow = nBottomRightTileRow;
1650 nTopLeftTileCol = std::max<sal_Int32>(nTopLeftTileCol, 0);
1651 nTopLeftTileRow = std::max<sal_Int32>(nTopLeftTileRow, 0);
1652 nTopLeftTileColOrigin = o3tl::convert(nTopLeftTileColOrigin, o3tl::Length::px, o3tl::Length::twip);
1653 nTopLeftTileRowOrigin = o3tl::convert(nTopLeftTileRowOrigin, o3tl::Length::px, o3tl::Length::twip);
1655 // Checkout -> 'rDoc.ExtendMerge' ... if we miss merged cells.
1657 // Origin must be the offset of the first col and row
1658 // containing our top-left pixel.
1659 const MapMode aOriginalMode = rDevice.GetMapMode();
1660 MapMode aAbsMode = aOriginalMode;
1661 const Point aOrigin(-nTopLeftTileColOrigin, -nTopLeftTileRowOrigin);
1662 aAbsMode.SetOrigin(aOrigin);
1663 rDevice.SetMapMode(aAbsMode);
1665 ScTableInfo aTabInfo(nEndRow + 3);
1666 rDoc.FillInfo(aTabInfo, nTopLeftTileCol, nTopLeftTileRow,
1667 nBottomRightTileCol, nBottomRightTileRow,
1668 nTab, fPPTX, fPPTY, false, false);
1670 // FIXME: is this called some
1671 // Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
1673 ScOutputData aOutputData(&rDevice, OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
1674 -nTopLeftTileColOffset,
1675 -nTopLeftTileRowOffset,
1676 nTopLeftTileCol, nTopLeftTileRow,
1677 nBottomRightTileCol, nBottomRightTileRow,
1678 fPPTX, fPPTY, nullptr, nullptr);
1680 // setup the SdrPage so that drawinglayer works correctly
1681 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1682 if (pModel)
1684 bool bPrintTwipsMsgs = comphelper::LibreOfficeKit::isCompatFlagSet(
1685 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
1686 if (!mpLOKDrawView)
1688 mpLOKDrawView.reset(bPrintTwipsMsgs ?
1689 new ScLOKDrawView(
1690 &rDevice,
1691 mrViewData) :
1692 new FmFormView(
1693 *pModel,
1694 &rDevice));
1697 mpLOKDrawView->SetNegativeX(bLayoutRTL);
1698 mpLOKDrawView->ShowSdrPage(mpLOKDrawView->GetModel()->GetPage(nTab));
1699 aOutputData.SetDrawView(mpLOKDrawView.get());
1700 aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
1703 // draw the content
1704 DrawContent(rDevice, aTabInfo, aOutputData, true);
1705 rDevice.SetMapMode(aOriginalMode);
1707 // Paint the chart(s) in edit mode.
1708 LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight,
1709 nTilePosX, nTilePosY, nTileWidth, nTileHeight, bLayoutRTL);
1711 rDevice.SetMapMode(aOriginalMode);
1713 // Flag drawn formula cells "unchanged".
1714 rDoc.ResetChanged(ScRange(nTopLeftTileCol, nTopLeftTileRow, nTab, nBottomRightTileCol, nBottomRightTileRow, nTab));
1715 rDoc.PrepareFormulaCalc();
1717 mrViewData.SetZoom(origZoomX, origZoomY, true);
1718 if (bChangeZoom)
1720 if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
1721 pDrawView->resetGridOffsetsForAllSdrPageViews();
1724 if (bLayoutRTL)
1726 Bitmap aCellBMP = rDevice.GetBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight));
1727 aCellBMP.Mirror(BmpMirrorFlags::Horizontal);
1728 rDevice.DrawBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight), aCellBMP);
1732 void ScGridWindow::LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart)
1734 tools::Rectangle aRectangle;
1735 tools::Rectangle* pResultRectangle;
1736 if (!pRectangle)
1737 pResultRectangle = nullptr;
1738 else
1740 aRectangle = *pRectangle;
1741 // When dragging shapes the map mode is disabled.
1742 if (IsMapModeEnabled())
1744 if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
1746 aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
1749 else
1750 aRectangle = PixelToLogic(aRectangle, MapMode(MapUnit::MapTwip));
1751 pResultRectangle = &aRectangle;
1754 // Trim invalidation rectangle overlapping negative X region in RTL mode.
1755 if (pResultRectangle && pResultRectangle->Left() < 0
1756 && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
1758 pResultRectangle->SetLeft(0);
1759 if (pResultRectangle->Right() < 0)
1760 pResultRectangle->SetRight(0);
1763 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
1764 SfxLokHelper::notifyInvalidation(pViewShell, nPart, pResultRectangle);
1767 void ScGridWindow::LogicInvalidate(const tools::Rectangle* pRectangle)
1769 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
1770 LogicInvalidatePart(pRectangle, pViewShell->getPart());
1773 bool ScGridWindow::InvalidateByForeignEditView(EditView* pEditView)
1775 if (!pEditView)
1776 return false;
1778 auto* pGridWin = dynamic_cast<ScGridWindow*>(pEditView->GetWindow());
1779 if (!pGridWin)
1780 return false;
1782 const ScViewData& rViewData = pGridWin->getViewData();
1783 tools::Long nRefTabNo = rViewData.GetRefTabNo();
1784 tools::Long nX = rViewData.GetCurXForTab(nRefTabNo);
1785 tools::Long nY = rViewData.GetCurYForTab(nRefTabNo);
1787 tools::Rectangle aPixRect = getViewData().GetEditArea(eWhich, nX, nY, this, nullptr, true);
1788 tools::Rectangle aLogicRect = PixelToLogic(aPixRect, getViewData().GetLogicMode());
1789 Invalidate(pEditView->IsNegativeX() ? lcl_negateRectX(aLogicRect) : aLogicRect);
1791 return true;
1794 void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY)
1796 ScTabView* pTabView = mrViewData.GetView();
1797 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
1798 ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewShell);
1800 if (pInputHandler && pInputHandler->IsInputMode())
1802 // we need to switch off the editeng
1803 ScTabView::UpdateInputLine();
1804 pViewShell->UpdateInputHandler();
1807 if (nType == LOK_SETTEXTSELECTION_RESET)
1809 pTabView->DoneBlockMode();
1810 return;
1813 // obtain the current selection
1814 ScRangeList aRangeList = mrViewData.GetMarkData().GetMarkedRanges();
1816 SCCOL nCol1, nCol2;
1817 SCROW nRow1, nRow2;
1818 SCTAB nTab1, nTab2;
1820 bool bWasEmpty = false;
1821 if (aRangeList.empty())
1823 nCol1 = nCol2 = mrViewData.GetCurX();
1824 nRow1 = nRow2 = mrViewData.GetCurY();
1825 bWasEmpty = true;
1827 else
1828 aRangeList.Combine().GetVars(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1830 // convert the coordinates to column/row
1831 SCCOL nNewPosX;
1832 SCROW nNewPosY;
1833 SCTAB nTab = mrViewData.GetTabNo();
1834 mrViewData.GetPosFromPixel(nPixelX, nPixelY, eWhich, nNewPosX, nNewPosY);
1836 // change the selection
1837 switch (nType)
1839 case LOK_SETTEXTSELECTION_START:
1840 if (nNewPosX != nCol1 || nNewPosY != nRow1 || bWasEmpty)
1842 pTabView->SetCursor(nNewPosX, nNewPosY);
1843 pTabView->DoneBlockMode();
1844 pTabView->InitBlockMode(nNewPosX, nNewPosY, nTab, true);
1845 pTabView->MarkCursor(nCol2, nRow2, nTab);
1847 break;
1848 case LOK_SETTEXTSELECTION_END:
1849 if (nNewPosX != nCol2 || nNewPosY != nRow2 || bWasEmpty)
1851 pTabView->SetCursor(nCol1, nRow1);
1852 pTabView->DoneBlockMode();
1853 pTabView->InitBlockMode(nCol1, nRow1, nTab, true);
1854 pTabView->MarkCursor(nNewPosX, nNewPosY, nTab);
1856 break;
1857 default:
1858 assert(false);
1859 break;
1863 void ScGridWindow::CheckNeedsRepaint()
1865 // called at the end of painting, and from timer after background text width calculation
1867 if (!bNeedsRepaint)
1868 return;
1870 bNeedsRepaint = false;
1871 if (aRepaintPixel.IsEmpty())
1872 Invalidate();
1873 else
1874 Invalidate(PixelToLogic(aRepaintPixel));
1875 aRepaintPixel = tools::Rectangle();
1877 // selection function in status bar might also be invalid
1878 SfxBindings& rBindings = mrViewData.GetBindings();
1879 rBindings.Invalidate( SID_STATUS_SUM );
1880 rBindings.Invalidate( SID_ATTR_SIZE );
1881 rBindings.Invalidate( SID_TABLE_CELL );
1884 void ScGridWindow::DrawHiddenIndicator( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext)
1886 ScDocument& rDoc = mrViewData.GetDocument();
1887 SCTAB nTab = mrViewData.GetTabNo();
1888 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1889 const svtools::ColorConfigValue aColorValue = rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL);
1890 if (aColorValue.bIsVisible) {
1891 rRenderContext.SetLineColor(aColorValue.nColor);
1892 LineInfo aLineInfo(LineStyle::Dash, 2);
1893 aLineInfo.SetDashCount(0);
1894 aLineInfo.SetDotCount(1);
1895 aLineInfo.SetDistance(15);
1896 // round caps except when running VCL_PLUGIN=gen due to a performance issue
1897 // https://bugs.documentfoundation.org/show_bug.cgi?id=128258#c14
1898 if (mrViewData.GetActiveWin()->GetSystemData()->toolkit != SystemEnvData::Toolkit::Gen)
1899 aLineInfo.SetLineCap(css::drawing::LineCap_ROUND);
1900 aLineInfo.SetDotLen(1);
1901 for (int i=nX1; i<nX2; i++) {
1902 if (rDoc.ColHidden(i,nTab) && (i<rDoc.MaxCol() ? !rDoc.ColHidden(i+1,nTab) : true)) {
1903 Point aStart = mrViewData.GetScrPos(i, nY1, eWhich, true );
1904 Point aEnd = mrViewData.GetScrPos(i, nY2, eWhich, true );
1905 rRenderContext.DrawLine(aStart,aEnd,aLineInfo);
1908 for (int i=nY1; i<nY2; i++) {
1909 if (rDoc.RowHidden(i,nTab) && (i<rDoc.MaxRow() ? !rDoc.RowHidden(i+1,nTab) : true)) {
1910 Point aStart = mrViewData.GetScrPos(nX1, i, eWhich, true );
1911 Point aEnd = mrViewData.GetScrPos(nX2, i, eWhich, true );
1912 rRenderContext.DrawLine(aStart,aEnd,aLineInfo);
1915 } //visible
1918 void ScGridWindow::DrawPagePreview( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext)
1920 ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData();
1921 if (!pPageData)
1922 return;
1924 ScDocument& rDoc = mrViewData.GetDocument();
1925 SCTAB nTab = mrViewData.GetTabNo();
1926 Size aWinSize = GetOutputSizePixel();
1927 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1928 Color aManual( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor );
1929 Color aAutomatic( rColorCfg.GetColorValue(svtools::CALCPAGEBREAK).nColor );
1931 OUString aPageStr = ScResId( STR_PGNUM );
1932 if ( nPageScript == SvtScriptType::NONE )
1934 // get script type of translated "Page" string only once
1935 nPageScript = rDoc.GetStringScriptType( aPageStr );
1936 if (nPageScript == SvtScriptType::NONE)
1937 nPageScript = ScGlobal::GetDefaultScriptType();
1940 vcl::Font aFont;
1941 std::unique_ptr<ScEditEngineDefaulter> pEditEng;
1942 const ScPatternAttr& rDefPattern = rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
1943 if ( nPageScript == SvtScriptType::LATIN )
1945 // use single font and call DrawText directly
1946 rDefPattern.fillFontOnly(aFont);
1947 aFont.SetColor(COL_LIGHTGRAY);
1948 // font size is set as needed
1950 else
1952 // use EditEngine to draw mixed-script string
1953 pEditEng.reset(new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ));
1954 pEditEng->SetRefMapMode(rRenderContext.GetMapMode());
1955 auto pEditDefaults = std::make_unique<SfxItemSet>( pEditEng->GetEmptyItemSet() );
1956 rDefPattern.FillEditItemSet( pEditDefaults.get() );
1957 pEditDefaults->Put( SvxColorItem( COL_LIGHTGRAY, EE_CHAR_COLOR ) );
1958 pEditEng->SetDefaults( std::move(pEditDefaults) );
1961 sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
1962 for (sal_uInt16 nPos=0; nPos<nCount; nPos++)
1964 ScPrintRangeData& rData = pPageData->GetData(nPos);
1965 ScRange aRange = rData.GetPrintRange();
1966 if ( aRange.aStart.Col() <= nX2+1 && aRange.aEnd.Col()+1 >= nX1 &&
1967 aRange.aStart.Row() <= nY2+1 && aRange.aEnd.Row()+1 >= nY1 )
1969 // 3 pixel frame around the print area
1970 // (middle pixel on the grid lines)
1972 rRenderContext.SetLineColor();
1973 if (rData.IsAutomatic())
1974 rRenderContext.SetFillColor( aAutomatic );
1975 else
1976 rRenderContext.SetFillColor( aManual );
1978 Point aStart = mrViewData.GetScrPos(
1979 aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
1980 Point aEnd = mrViewData.GetScrPos(
1981 aRange.aEnd.Col() + 1, aRange.aEnd.Row() + 1, eWhich, true );
1982 aStart.AdjustX( -2 );
1983 aStart.AdjustY( -2 );
1985 // Prevent overflows:
1986 if ( aStart.X() < -10 ) aStart.setX( -10 );
1987 if ( aStart.Y() < -10 ) aStart.setY( -10 );
1988 if ( aEnd.X() > aWinSize.Width() + 10 )
1989 aEnd.setX( aWinSize.Width() + 10 );
1990 if ( aEnd.Y() > aWinSize.Height() + 10 )
1991 aEnd.setY( aWinSize.Height() + 10 );
1993 rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aEnd.X(),aStart.Y()+2) ) );
1994 rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aStart.X()+2,aEnd.Y()) ) );
1995 rRenderContext.DrawRect( tools::Rectangle( Point(aStart.X(),aEnd.Y()-2), aEnd ) );
1996 rRenderContext.DrawRect( tools::Rectangle( Point(aEnd.X()-2,aStart.Y()), aEnd ) );
1998 // Page breaks
1999 //! Display differently (dashed ????)
2001 size_t nColBreaks = rData.GetPagesX();
2002 const SCCOL* pColEnd = rData.GetPageEndX();
2003 size_t nColPos;
2004 for (nColPos=0; nColPos+1<nColBreaks; nColPos++)
2006 SCCOL nBreak = pColEnd[nColPos]+1;
2007 if ( nBreak >= nX1 && nBreak <= nX2+1 )
2009 //! Search for hidden
2010 if (rDoc.HasColBreak(nBreak, nTab) & ScBreakType::Manual)
2011 rRenderContext.SetFillColor( aManual );
2012 else
2013 rRenderContext.SetFillColor( aAutomatic );
2014 Point aBreak = mrViewData.GetScrPos(
2015 nBreak, aRange.aStart.Row(), eWhich, true );
2016 rRenderContext.DrawRect( tools::Rectangle( aBreak.X()-1, aStart.Y(), aBreak.X(), aEnd.Y() ) );
2020 size_t nRowBreaks = rData.GetPagesY();
2021 const SCROW* pRowEnd = rData.GetPageEndY();
2022 size_t nRowPos;
2023 for (nRowPos=0; nRowPos+1<nRowBreaks; nRowPos++)
2025 SCROW nBreak = pRowEnd[nRowPos]+1;
2026 if ( nBreak >= nY1 && nBreak <= nY2+1 )
2028 //! Search for hidden
2029 if (rDoc.HasRowBreak(nBreak, nTab) & ScBreakType::Manual)
2030 rRenderContext.SetFillColor( aManual );
2031 else
2032 rRenderContext.SetFillColor( aAutomatic );
2033 Point aBreak = mrViewData.GetScrPos(
2034 aRange.aStart.Col(), nBreak, eWhich, true );
2035 rRenderContext.DrawRect( tools::Rectangle( aStart.X(), aBreak.Y()-1, aEnd.X(), aBreak.Y() ) );
2039 // Page numbers
2041 SCROW nPrStartY = aRange.aStart.Row();
2042 for (nRowPos=0; nRowPos<nRowBreaks; nRowPos++)
2044 SCROW nPrEndY = pRowEnd[nRowPos];
2045 if ( nPrEndY >= nY1 && nPrStartY <= nY2 )
2047 SCCOL nPrStartX = aRange.aStart.Col();
2048 for (nColPos=0; nColPos<nColBreaks; nColPos++)
2050 SCCOL nPrEndX = pColEnd[nColPos];
2051 if ( nPrEndX >= nX1 && nPrStartX <= nX2 )
2053 Point aPageStart = mrViewData.GetScrPos(
2054 nPrStartX, nPrStartY, eWhich, true );
2055 Point aPageEnd = mrViewData.GetScrPos(
2056 nPrEndX+1,nPrEndY+1, eWhich, true );
2058 tools::Long nPageNo = rData.GetFirstPage();
2059 if ( rData.IsTopDown() )
2060 nPageNo += static_cast<tools::Long>(nColPos)*nRowBreaks+nRowPos;
2061 else
2062 nPageNo += static_cast<tools::Long>(nRowPos)*nColBreaks+nColPos;
2064 OUString aThisPageStr = aPageStr.replaceFirst("%1", OUString::number(nPageNo));
2066 if ( pEditEng )
2068 // find right font size with EditEngine
2069 tools::Long nHeight = 100;
2070 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
2071 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
2072 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
2073 pEditEng->SetTextCurrentDefaults( aThisPageStr );
2074 Size aSize100( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() );
2076 // 40% of width or 60% of height
2077 tools::Long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width();
2078 tools::Long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height();
2079 nHeight = std::min(nSizeX,nSizeY);
2080 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
2081 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
2082 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
2084 // centered output with EditEngine
2085 Size aTextSize( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() );
2086 Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2,
2087 (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 );
2088 pEditEng->Draw(rRenderContext, aPos);
2090 else
2092 // find right font size for DrawText
2093 aFont.SetFontSize( Size( 0,100 ) );
2094 rRenderContext.SetFont( aFont );
2096 Size aSize100(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() );
2097 if (aSize100.Width() && aSize100.Height())
2099 // 40% of width or 60% of height
2100 tools::Long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width();
2101 tools::Long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height();
2102 aFont.SetFontSize( Size( 0,std::min(nSizeX,nSizeY) ) );
2103 rRenderContext.SetFont( aFont );
2106 // centered output with DrawText
2107 Size aTextSize(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() );
2108 Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2,
2109 (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 );
2110 rRenderContext.DrawText( aPos, aThisPageStr );
2113 nPrStartX = nPrEndX + 1;
2116 nPrStartY = nPrEndY + 1;
2122 void ScGridWindow::DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo, OutputDevice* pContentDev, const ScLokRTLContext* pLokRTLContext)
2124 aComboButton.SetOutputDevice( pContentDev );
2126 ScDocument& rDoc = mrViewData.GetDocument();
2127 ScDPFieldButton aCellBtn(pContentDev, &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc);
2129 SCCOL nCol;
2130 SCROW nRow;
2131 SCSIZE nArrY;
2132 SCSIZE nQuery;
2133 SCTAB nTab = mrViewData.GetTabNo();
2134 ScDBData* pDBData = nullptr;
2135 std::unique_ptr<ScQueryParam> pQueryParam;
2137 RowInfo* pRowInfo = rTabInfo.mpRowInfo.get();
2138 sal_uInt16 nArrCount = rTabInfo.mnArrCount;
2140 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
2142 Point aOldPos = aComboButton.GetPosPixel(); // store state for MouseDown/Up
2143 Size aOldSize = aComboButton.GetSizePixel();
2145 for (nArrY=1; nArrY+1<nArrCount; nArrY++)
2147 if ( pRowInfo[nArrY].bAutoFilter && pRowInfo[nArrY].bChanged )
2149 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2151 nRow = pThisRowInfo->nRowNo;
2153 for (nCol=nX1; nCol<=nX2; nCol++)
2155 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
2156 //if several columns merged on a row, there should be only one auto button at the end of the columns.
2157 //if several rows merged on a column, the button may be in the middle, so "!pInfo->bVOverlapped" should not be used
2158 if ( pInfo->bAutoFilter && !pInfo->bHOverlapped )
2160 if (!pQueryParam)
2161 pQueryParam.reset(new ScQueryParam);
2163 bool bNewData = true;
2164 if (pDBData)
2166 SCCOL nStartCol;
2167 SCROW nStartRow;
2168 SCCOL nEndCol;
2169 SCROW nEndRow;
2170 SCTAB nAreaTab;
2171 pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
2172 if ( nCol >= nStartCol && nCol <= nEndCol &&
2173 nRow >= nStartRow && nRow <= nEndRow )
2174 bNewData = false;
2176 if (bNewData)
2178 pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
2179 if (pDBData)
2180 pDBData->GetQueryParam( *pQueryParam );
2181 else
2183 // can also be part of DataPilot table
2187 // pQueryParam can only include MAXQUERY entries
2189 bool bArrowState = false;
2190 if (pQueryParam->bInplace)
2192 SCSIZE nCount = pQueryParam->GetEntryCount();
2193 for (nQuery = 0; nQuery < nCount; ++nQuery)
2195 // Do no restrict to EQUAL here
2196 // (Column head should become blue also when ">1")
2197 const ScQueryEntry& rEntry = pQueryParam->GetEntry(nQuery);
2198 if (rEntry.bDoQuery && rEntry.nField == nCol)
2200 bArrowState = true;
2201 break;
2206 tools::Long nSizeX;
2207 tools::Long nSizeY;
2208 SCCOL nStartCol= nCol;
2209 SCROW nStartRow = nRow;
2210 //if address(nCol,nRow) is not the start pos of the merge area, the value of the nSizeX will be incorrect, it will be the length of the cell.
2211 //should first get the start pos of the merge area, then get the nSizeX through the start pos.
2212 rDoc.ExtendOverlapped(nStartCol, nStartRow,nCol, nRow, nTab);//get nStartCol,nStartRow
2213 mrViewData.GetMergeSizePixel( nStartCol, nStartRow, nSizeX, nSizeY );//get nSizeX
2214 nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
2215 Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
2216 if (pLokRTLContext)
2217 aScrPos.setX(pLokRTLContext->docToTilePos(aScrPos.X()));
2219 aCellBtn.setBoundingBox(aScrPos, Size(nSizeX-1, nSizeY-1), bLayoutRTL);
2220 aCellBtn.setPopupLeft(bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL
2221 aCellBtn.setDrawBaseButton(false);
2222 aCellBtn.setDrawPopupButton(true);
2223 aCellBtn.setHasHiddenMember(bArrowState);
2224 aCellBtn.draw();
2229 if ( (pRowInfo[nArrY].bPivotToggle || pRowInfo[nArrY].bPivotButton) && pRowInfo[nArrY].bChanged )
2231 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2232 nRow = pThisRowInfo->nRowNo;
2233 for (nCol=nX1; nCol<=nX2; nCol++)
2235 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
2236 if (pInfo->bHOverlapped || pInfo->bVOverlapped)
2237 continue;
2239 Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
2240 tools::Long nSizeX;
2241 tools::Long nSizeY;
2242 mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
2243 tools::Long nPosX = aScrPos.X();
2244 tools::Long nPosY = aScrPos.Y();
2245 // bLayoutRTL is handled in setBoundingBox
2247 bool bDrawToggle = pInfo->bPivotCollapseButton || pInfo->bPivotExpandButton;
2248 if (!bDrawToggle)
2250 OUString aStr = rDoc.GetString(nCol, nRow, nTab);
2251 aCellBtn.setText(aStr);
2254 sal_uInt16 nIndent = 0;
2255 if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
2256 nIndent = pIndentItem->GetValue();
2257 aCellBtn.setBoundingBox(Point(nPosX, nPosY), Size(nSizeX-1, nSizeY-1), bLayoutRTL);
2258 aCellBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
2259 aCellBtn.setDrawBaseButton(pInfo->bPivotButton);
2260 aCellBtn.setDrawPopupButton(pInfo->bPivotPopupButton);
2261 aCellBtn.setDrawPopupButtonMulti(pInfo->bPivotPopupButtonMulti);
2262 aCellBtn.setDrawToggleButton(bDrawToggle, pInfo->bPivotCollapseButton, nIndent);
2263 aCellBtn.setHasHiddenMember(pInfo->bFilterActive);
2264 aCellBtn.draw();
2268 if ( !comphelper::LibreOfficeKit::isActive() && bListValButton && pRowInfo[nArrY].nRowNo == aListValPos.Row() && pRowInfo[nArrY].bChanged )
2270 tools::Rectangle aRect = GetListValButtonRect( aListValPos );
2271 aComboButton.SetPosPixel( aRect.TopLeft() );
2272 aComboButton.SetSizePixel( aRect.GetSize() );
2273 pContentDev->SetClipRegion(vcl::Region(aRect));
2274 aComboButton.Draw();
2275 pContentDev->SetClipRegion(); // always called from Draw() without clip region
2276 aComboButton.SetPosPixel( aOldPos ); // restore old state
2277 aComboButton.SetSizePixel( aOldSize ); // for MouseUp/Down (AutoFilter)
2281 pQueryParam.reset();
2282 aComboButton.SetOutputDevice( GetOutDev() );
2285 tools::Rectangle ScGridWindow::GetListValButtonRect( const ScAddress& rButtonPos )
2287 ScDocument& rDoc = mrViewData.GetDocument();
2288 SCTAB nTab = mrViewData.GetTabNo();
2289 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
2290 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2292 ScDDComboBoxButton aButton( GetOutDev() ); // for optimal size
2293 Size aBtnSize = aButton.GetSizePixel();
2295 SCCOL nCol = rButtonPos.Col();
2296 SCROW nRow = rButtonPos.Row();
2298 tools::Long nCellSizeX; // width of this cell, including merged
2299 tools::Long nDummy;
2300 mrViewData.GetMergeSizePixel( nCol, nRow, nCellSizeX, nDummy );
2302 // for height, only the cell's row is used, excluding merged cells
2303 tools::Long nCellSizeY = ScViewData::ToPixel( rDoc.GetRowHeight( nRow, nTab ), mrViewData.GetPPTY() );
2304 tools::Long nAvailable = nCellSizeX;
2306 // left edge of next cell if there is a non-hidden next column
2307 SCCOL nNextCol = nCol + 1;
2308 const ScMergeAttr* pMerge = rDoc.GetAttr( nCol,nRow,nTab, ATTR_MERGE );
2309 if ( pMerge->GetColMerge() > 1 )
2310 nNextCol = nCol + pMerge->GetColMerge(); // next cell after the merged area
2311 while ( nNextCol <= rDoc.MaxCol() && rDoc.ColHidden(nNextCol, nTab) )
2312 ++nNextCol;
2313 bool bNextCell = ( nNextCol <= rDoc.MaxCol() );
2314 if ( bNextCell )
2315 nAvailable = ScViewData::ToPixel( rDoc.GetColWidth( nNextCol, nTab ), mrViewData.GetPPTX() );
2317 if ( nAvailable < aBtnSize.Width() )
2318 aBtnSize.setWidth( nAvailable );
2319 if ( nCellSizeY < aBtnSize.Height() )
2320 aBtnSize.setHeight( nCellSizeY );
2322 Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich, true );
2323 aPos.AdjustX(nCellSizeX * nLayoutSign ); // start of next cell
2324 if (!bNextCell)
2325 aPos.AdjustX( -(aBtnSize.Width() * nLayoutSign) ); // right edge of cell if next cell not available
2326 aPos.AdjustY(nCellSizeY - aBtnSize.Height() );
2327 // X remains at the left edge
2329 if ( bLayoutRTL )
2330 aPos.AdjustX( -(aBtnSize.Width()-1) ); // align right edge of button with cell border
2332 return tools::Rectangle( aPos, aBtnSize );
2335 bool ScGridWindow::IsAutoFilterActive( SCCOL nCol, SCROW nRow, SCTAB nTab )
2337 ScDocument& rDoc = mrViewData.GetDocument();
2338 ScDBData* pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
2339 ScQueryParam aQueryParam;
2341 if ( pDBData )
2342 pDBData->GetQueryParam( aQueryParam );
2343 else
2345 OSL_FAIL("Auto filter button without DBData");
2348 bool bSimpleQuery = true;
2349 bool bColumnFound = false;
2350 SCSIZE nQuery;
2352 if ( !aQueryParam.bInplace )
2353 bSimpleQuery = false;
2355 // aQueryParam can only include MAXQUERY entries
2357 SCSIZE nCount = aQueryParam.GetEntryCount();
2358 for (nQuery = 0; nQuery < nCount && bSimpleQuery; ++nQuery)
2359 if ( aQueryParam.GetEntry(nQuery).bDoQuery )
2361 if (aQueryParam.GetEntry(nQuery).nField == nCol)
2362 bColumnFound = true;
2364 if (nQuery > 0)
2365 if (aQueryParam.GetEntry(nQuery).eConnect != SC_AND)
2366 bSimpleQuery = false;
2369 return ( bSimpleQuery && bColumnFound );
2372 void ScGridWindow::GetSelectionRects( ::std::vector< tools::Rectangle >& rPixelRects ) const
2374 GetPixelRectsFor( mrViewData.GetMarkData(), rPixelRects );
2377 void ScGridWindow::GetSelectionRectsPrintTwips(::std::vector< tools::Rectangle >& rRects) const
2379 GetRectsAnyFor(mrViewData.GetMarkData(), rRects, true);
2382 /// convert rMarkData into pixel rectangles for this view
2383 void ScGridWindow::GetPixelRectsFor( const ScMarkData &rMarkData,
2384 ::std::vector< tools::Rectangle >& rPixelRects ) const
2386 GetRectsAnyFor(rMarkData, rPixelRects, false);
2389 void ScGridWindow::GetRectsAnyFor(const ScMarkData &rMarkData,
2390 ::std::vector< tools::Rectangle >& rRects,
2391 bool bInPrintTwips) const
2393 ScDocument& rDoc = mrViewData.GetDocument();
2394 SCTAB nTab = mrViewData.GetTabNo();
2395 double nPPTX = mrViewData.GetPPTX();
2396 double nPPTY = mrViewData.GetPPTY();
2397 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
2398 // LOK clients needs exact document coordinates, so don't horizontally mirror them.
2399 tools::Long nLayoutSign = (!comphelper::LibreOfficeKit::isActive() && bLayoutRTL) ? -1 : 1;
2401 ScMarkData aMultiMark( rMarkData );
2402 aMultiMark.SetMarking( false );
2404 if (!aMultiMark.IsMultiMarked())
2406 // simple range case - simplify calculation
2407 const ScRange& aSimpleRange = aMultiMark.GetMarkArea();
2409 aMultiMark.MarkToMulti();
2410 if ( !aMultiMark.IsMultiMarked() )
2411 return;
2413 SCCOL nX1 = aSimpleRange.aStart.Col();
2414 SCROW nY1 = aSimpleRange.aStart.Row();
2415 SCCOL nX2 = aSimpleRange.aEnd.Col();
2416 SCROW nY2 = aSimpleRange.aEnd.Row();
2418 PutInOrder( nX1, nX2 );
2419 PutInOrder( nY1, nY2 );
2421 SCCOL nPosX = mrViewData.GetPosX( eHWhich );
2422 SCROW nPosY = mrViewData.GetPosY( eVWhich );
2423 // is the selection visible at all?
2424 if (nX2 < nPosX || nY2 < nPosY)
2425 return;
2427 Point aScrStartPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) :
2428 mrViewData.GetScrPos(nX1, nY1, eWhich);
2430 tools::Long nStartX = aScrStartPos.X();
2431 tools::Long nStartY = aScrStartPos.Y();
2433 Point aScrEndPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX2, nY2) :
2434 mrViewData.GetScrPos(nX2, nY2, eWhich);
2436 tools::Long nWidthTwips = rDoc.GetColWidth(nX2, nTab);
2437 const tools::Long nWidth = bInPrintTwips ?
2438 nWidthTwips : ScViewData::ToPixel(nWidthTwips, nPPTX);
2439 tools::Long nEndX = aScrEndPos.X() + (nWidth - 1) * nLayoutSign;
2441 sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY2, nTab );
2442 const tools::Long nHeight = bInPrintTwips ?
2443 nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY);
2444 tools::Long nEndY = aScrEndPos.Y() + nHeight - 1;
2446 ScInvertMerger aInvert( &rRects );
2447 aInvert.AddRect( tools::Rectangle( nStartX, nStartY, nEndX, nEndY ) );
2449 return;
2452 aMultiMark.MarkToMulti();
2453 if ( !aMultiMark.IsMultiMarked() )
2454 return;
2455 const ScRange& aMultiRange = aMultiMark.GetMultiMarkArea();
2456 SCCOL nX1 = aMultiRange.aStart.Col();
2457 SCROW nY1 = aMultiRange.aStart.Row();
2458 SCCOL nX2 = aMultiRange.aEnd.Col();
2459 SCROW nY2 = aMultiRange.aEnd.Row();
2461 PutInOrder( nX1, nX2 );
2462 PutInOrder( nY1, nY2 );
2464 SCCOL nTestX2 = nX2;
2465 SCROW nTestY2 = nY2;
2467 rDoc.ExtendMerge( nX1,nY1, nTestX2,nTestY2, nTab );
2469 SCCOL nPosX = mrViewData.GetPosX( eHWhich );
2470 SCROW nPosY = mrViewData.GetPosY( eVWhich );
2471 // is the selection visible at all?
2472 if (nTestX2 < nPosX || nTestY2 < nPosY)
2473 return;
2474 SCCOL nRealX1 = nX1;
2475 if (nX1 < nPosX)
2476 nX1 = nPosX;
2477 if (nY1 < nPosY)
2478 nY1 = nPosY;
2480 if (!comphelper::LibreOfficeKit::isActive())
2482 // limit the selection to only what is visible on the screen
2483 SCCOL nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich);
2484 if (nXRight > rDoc.MaxCol())
2485 nXRight = rDoc.MaxCol();
2487 SCROW nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich);
2488 if (nYBottom > rDoc.MaxRow())
2489 nYBottom = rDoc.MaxRow();
2491 // is the selection visible at all?
2492 if (nX1 > nXRight || nY1 > nYBottom)
2493 return;
2495 if (nX2 > nXRight)
2496 nX2 = nXRight;
2497 if (nY2 > nYBottom)
2498 nY2 = nYBottom;
2500 else
2502 SCCOL nMaxTiledCol;
2503 SCROW nMaxTiledRow;
2504 rDoc.GetTiledRenderingArea(nTab, nMaxTiledCol, nMaxTiledRow);
2506 if (nX2 > nMaxTiledCol)
2507 nX2 = nMaxTiledCol;
2508 if (nY2 > nMaxTiledRow)
2509 nY2 = nMaxTiledRow;
2512 ScInvertMerger aInvert( &rRects );
2514 Point aScrPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) :
2515 mrViewData.GetScrPos(nX1, nY1, eWhich);
2516 tools::Long nScrY = aScrPos.Y();
2517 bool bWasHidden = false;
2518 for (SCROW nY=nY1; nY<=nY2; nY++)
2520 bool bFirstRow = ( nY == nPosY ); // first visible row?
2521 bool bDoHidden = false; // repeat hidden ?
2522 sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY,nTab );
2523 bool bDoRow = ( nHeightTwips != 0 );
2524 if (bDoRow)
2526 if (bWasHidden) // test hidden merge
2528 bDoHidden = true;
2529 bDoRow = true;
2532 bWasHidden = false;
2534 else
2536 bWasHidden = true;
2537 if (nY==nY2)
2538 bDoRow = true; // last cell of the block
2541 if ( bDoRow )
2543 SCCOL nLoopEndX = nX2;
2544 if (nX2 < nX1) // the rest of the merge
2546 SCCOL nStartX = nX1;
2547 while ( rDoc.GetAttr(nStartX,nY,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped() )
2548 --nStartX;
2549 if (nStartX <= nX2)
2550 nLoopEndX = nX1;
2553 const tools::Long nHeight = bInPrintTwips ?
2554 nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY);
2555 tools::Long nEndY = nScrY + nHeight - 1;
2556 tools::Long nScrX = aScrPos.X();
2557 for (SCCOL nX=nX1; nX<=nLoopEndX; nX++)
2559 tools::Long nWidth = rDoc.GetColWidth(nX, nTab);
2560 if (!bInPrintTwips)
2561 nWidth = ScViewData::ToPixel(nWidth, nPPTX);
2563 if ( nWidth > 0 )
2565 tools::Long nEndX = nScrX + ( nWidth - 1 ) * nLayoutSign;
2567 SCROW nThisY = nY;
2568 const ScPatternAttr* pPattern = rDoc.GetPattern( nX, nY, nTab );
2569 const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
2570 if ( pMergeFlag->IsVerOverlapped() && ( bDoHidden || bFirstRow ) )
2572 while ( pMergeFlag->IsVerOverlapped() && nThisY > 0 &&
2573 (rDoc.RowHidden(nThisY-1, nTab) || bFirstRow) )
2575 --nThisY;
2576 pPattern = rDoc.GetPattern( nX, nThisY, nTab );
2577 pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
2581 // only the rest of the merged is seen ?
2582 SCCOL nThisX = nX;
2583 if ( pMergeFlag->IsHorOverlapped() && nX == nPosX && nX > nRealX1 )
2585 while ( pMergeFlag->IsHorOverlapped() )
2587 --nThisX;
2588 pPattern = rDoc.GetPattern( nThisX, nThisY, nTab );
2589 pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
2593 if ( aMultiMark.IsCellMarked( nThisX, nThisY, true ) )
2595 if ( !pMergeFlag->IsOverlapped() )
2597 const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
2598 if (pMerge->GetColMerge() > 0 || pMerge->GetRowMerge() > 0)
2600 const SCCOL nEndColMerge = nThisX + pMerge->GetColMerge();
2601 const SCROW nEndRowMerge = nThisY + pMerge->GetRowMerge();
2602 Point aEndPos = bInPrintTwips ?
2603 mrViewData.GetPrintTwipsPos(nEndColMerge, nEndRowMerge) :
2604 mrViewData.GetScrPos(nEndColMerge, nEndRowMerge, eWhich);
2605 if ( aEndPos.X() * nLayoutSign > nScrX * nLayoutSign && aEndPos.Y() > nScrY )
2607 aInvert.AddRect( tools::Rectangle( nScrX,nScrY,
2608 aEndPos.X()-nLayoutSign,aEndPos.Y()-1 ) );
2611 else if ( nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY )
2613 aInvert.AddRect( tools::Rectangle( nScrX,nScrY,nEndX,nEndY ) );
2618 nScrX = nEndX + nLayoutSign;
2621 nScrY = nEndY + 1;
2626 void ScGridWindow::DataChanged( const DataChangedEvent& rDCEvt )
2628 Window::DataChanged(rDCEvt);
2630 if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) ||
2631 (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
2632 (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2633 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2634 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2635 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
2636 return;
2638 if ( rDCEvt.GetType() == DataChangedEventType::FONTS && eWhich == mrViewData.GetActivePart() )
2639 mrViewData.GetDocShell()->UpdateFontList();
2641 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2642 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
2644 if ( eWhich == mrViewData.GetActivePart() ) // only once for the view
2646 ScTabView* pView = mrViewData.GetView();
2648 pView->RecalcPPT();
2650 // RepeatResize in case scroll bar sizes have changed
2651 pView->RepeatResize();
2652 pView->UpdateAllOverlays();
2654 // invalidate cell attribs in input handler, in case the
2655 // EditEngine BackgroundColor has to be changed
2656 if ( mrViewData.IsActive() )
2658 ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
2659 if (pHdl)
2660 pHdl->ForgetLastPattern();
2665 Invalidate();
2668 void ScGridWindow::initiatePageBreaks()
2670 bInitialPageBreaks = true;
2673 IMPL_LINK(ScGridWindow, InitiatePageBreaksTimer, Timer*, pTimer, void)
2675 if (pTimer != &maShowPageBreaksTimer)
2676 return;
2678 const ScViewOptions& rOpts = mrViewData.GetOptions();
2679 const bool bPage = rOpts.GetOption(VOPT_PAGEBREAKS);
2680 // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page
2681 // breaks is enabled, breaks should be visible. If the document is
2682 // opened the first time or a tab is activated the first time, the
2683 // breaks are not calculated yet, so this initialization is done here.
2684 if (bPage)
2686 const SCTAB nCurrentTab = mrViewData.GetTabNo();
2687 ScDocument& rDoc = mrViewData.GetDocument();
2688 const Size aPageSize = rDoc.GetPageSize(nCurrentTab);
2689 // Do not attempt to calculate a page size here if it is empty if
2690 // that involves counting pages.
2691 // An earlier implementation did
2692 // ScPrintFunc(pDocSh, pDocSh->GetPrinter(), nCurrentTab);
2693 // rDoc.SetPageSize(nCurrentTab, rDoc.GetPageSize(nCurrentTab));
2694 // which resulted in tremendous waiting times after having loaded
2695 // larger documents i.e. imported from CSV, in which UI is entirely
2696 // blocked. All time is spent under ScPrintFunc::CountPages() in
2697 // ScTable::ExtendPrintArea() in the loop that calls
2698 // MaybeAddExtraColumn() to do stuff for each text string content
2699 // cell (each row in each column). Maybe that can be optimized, or
2700 // obtaining page size without that overhead would be possible, but
2701 // as is calling that from here is a no-no so this is a quick
2702 // disable things.
2703 if (!aPageSize.IsEmpty())
2705 ScDocShell* pDocSh = mrViewData.GetDocShell();
2706 const bool bModified = pDocSh->IsModified();
2707 // Even setting the same size sets page size valid, so
2708 // UpdatePageBreaks() actually does something.
2709 rDoc.SetPageSize( nCurrentTab, aPageSize);
2710 rDoc.UpdatePageBreaks(nCurrentTab);
2711 pDocSh->PostPaint(0, 0, nCurrentTab, rDoc.MaxCol(), rDoc.MaxRow(), nCurrentTab, PaintPartFlags::Grid);
2712 pDocSh->SetModified(bModified);
2718 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */