File: | home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx |
Warning: | line 1397, column 9 Value stored to 'nEndCol' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | /* |
3 | * This file is part of the LibreOffice project. |
4 | * |
5 | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | * |
9 | * This file incorporates work covered by the following license notice: |
10 | * |
11 | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | * contributor license agreements. See the NOTICE file distributed |
13 | * with this work for additional information regarding copyright |
14 | * ownership. The ASF licenses this file to you under the Apache |
15 | * License, Version 2.0 (the "License"); you may not use this file |
16 | * except in compliance with the License. You may obtain a copy of |
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | */ |
19 | |
20 | #include <memory> |
21 | #include <scitems.hxx> |
22 | #include <editeng/eeitem.hxx> |
23 | |
24 | #include <svtools/colorcfg.hxx> |
25 | #include <editeng/colritem.hxx> |
26 | #include <editeng/editview.hxx> |
27 | #include <editeng/fhgtitem.hxx> |
28 | #include <sfx2/bindings.hxx> |
29 | #include <sfx2/printer.hxx> |
30 | #include <vcl/cursor.hxx> |
31 | #include <vcl/settings.hxx> |
32 | |
33 | #include <LibreOfficeKit/LibreOfficeKitEnums.h> |
34 | #include <comphelper/lok.hxx> |
35 | #include <sfx2/lokhelper.hxx> |
36 | |
37 | #include <svx/svdview.hxx> |
38 | #include <svx/svdpagv.hxx> |
39 | #include <svx/sdrpagewindow.hxx> |
40 | #include <svx/sdr/contact/objectcontactofpageview.hxx> |
41 | #include <svx/sdr/contact/viewobjectcontact.hxx> |
42 | #include <svx/sdr/contact/viewcontact.hxx> |
43 | #include <tabvwsh.hxx> |
44 | |
45 | #include <gridwin.hxx> |
46 | #include <viewdata.hxx> |
47 | #include <output.hxx> |
48 | #include <document.hxx> |
49 | #include <attrib.hxx> |
50 | #include <patattr.hxx> |
51 | #include <dbdata.hxx> |
52 | #include <docoptio.hxx> |
53 | #include <notemark.hxx> |
54 | #include <dbfunc.hxx> |
55 | #include <scmod.hxx> |
56 | #include <inputhdl.hxx> |
57 | #include <rfindlst.hxx> |
58 | #include <hiranges.hxx> |
59 | #include <pagedata.hxx> |
60 | #include <docpool.hxx> |
61 | #include <globstr.hrc> |
62 | #include <scresid.hxx> |
63 | #include <docsh.hxx> |
64 | #include <cbutton.hxx> |
65 | #include <invmerge.hxx> |
66 | #include <editutil.hxx> |
67 | #include <inputopt.hxx> |
68 | #include <fillinfo.hxx> |
69 | #include <dpcontrol.hxx> |
70 | #include <queryparam.hxx> |
71 | #include <queryentry.hxx> |
72 | #include <markdata.hxx> |
73 | #include <sc.hrc> |
74 | #include <vcl/virdev.hxx> |
75 | #include <svx/sdrpaintwindow.hxx> |
76 | #include <drwlayer.hxx> |
77 | |
78 | static void lcl_LimitRect( tools::Rectangle& rRect, const tools::Rectangle& rVisible ) |
79 | { |
80 | if ( rRect.Top() < rVisible.Top()-1 ) rRect.SetTop( rVisible.Top()-1 ); |
81 | if ( rRect.Bottom() > rVisible.Bottom()+1 ) rRect.SetBottom( rVisible.Bottom()+1 ); |
82 | |
83 | // The header row must be drawn also when the inner rectangle is not visible, |
84 | // that is why there is no return value anymore. |
85 | // When it is far away, then lcl_DrawOneFrame is not even called. |
86 | } |
87 | |
88 | static void lcl_DrawOneFrame( vcl::RenderContext* pDev, const tools::Rectangle& rInnerPixel, |
89 | const OUString& rTitle, const Color& rColor, bool bTextBelow, |
90 | double nPPTX, double nPPTY, const Fraction& rZoomY, |
91 | ScDocument& rDoc, ScViewData& rButtonViewData, bool bLayoutRTL ) |
92 | { |
93 | // rButtonViewData is only used to set the button size, |
94 | |
95 | tools::Rectangle aInner = rInnerPixel; |
96 | if ( bLayoutRTL ) |
97 | { |
98 | aInner.SetLeft( rInnerPixel.Right() ); |
99 | aInner.SetRight( rInnerPixel.Left() ); |
100 | } |
101 | |
102 | tools::Rectangle aVisible( Point(0,0), pDev->GetOutputSizePixel() ); |
103 | lcl_LimitRect( aInner, aVisible ); |
104 | |
105 | tools::Rectangle aOuter = aInner; |
106 | long nHor = static_cast<long>( SC_SCENARIO_HSPACE60 * nPPTX ); |
107 | long nVer = static_cast<long>( SC_SCENARIO_VSPACE50 * nPPTY ); |
108 | aOuter.AdjustLeft( -nHor ); |
109 | aOuter.AdjustRight(nHor ); |
110 | aOuter.AdjustTop( -nVer ); |
111 | aOuter.AdjustBottom(nVer ); |
112 | |
113 | // use ScPatternAttr::GetFont only for font size |
114 | vcl::Font aAttrFont; |
115 | rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN). |
116 | GetFont(aAttrFont,SC_AUTOCOL_BLACK,pDev,&rZoomY); |
117 | |
118 | // everything else from application font |
119 | vcl::Font aAppFont = pDev->GetSettings().GetStyleSettings().GetAppFont(); |
120 | aAppFont.SetFontSize( aAttrFont.GetFontSize() ); |
121 | |
122 | aAppFont.SetAlignment( ALIGN_TOP ); |
123 | pDev->SetFont( aAppFont ); |
124 | |
125 | Size aTextSize( pDev->GetTextWidth( rTitle ), pDev->GetTextHeight() ); |
126 | |
127 | if ( bTextBelow ) |
128 | aOuter.AdjustBottom(aTextSize.Height() ); |
129 | else |
130 | aOuter.AdjustTop( -(aTextSize.Height()) ); |
131 | |
132 | pDev->SetLineColor(); |
133 | pDev->SetFillColor( rColor ); |
134 | // left, top, right, bottom |
135 | pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aInner.Left(), aOuter.Bottom() ) ); |
136 | pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aOuter.Right(), aInner.Top() ) ); |
137 | pDev->DrawRect( tools::Rectangle( aInner.Right(), aOuter.Top(), aOuter.Right(), aOuter.Bottom() ) ); |
138 | pDev->DrawRect( tools::Rectangle( aOuter.Left(), aInner.Bottom(), aOuter.Right(), aOuter.Bottom() ) ); |
139 | |
140 | long nButtonY = bTextBelow ? aInner.Bottom() : aOuter.Top(); |
141 | |
142 | ScDDComboBoxButton aComboButton(pDev); |
143 | aComboButton.SetOptSizePixel(); |
144 | long nBWidth = long(aComboButton.GetSizePixel().Width() * rZoomY); |
145 | long nBHeight = nVer + aTextSize.Height() + 1; |
146 | Size aButSize( nBWidth, nBHeight ); |
147 | long nButtonPos = bLayoutRTL ? aOuter.Left() : aOuter.Right()-nBWidth+1; |
148 | aComboButton.Draw( Point(nButtonPos, nButtonY), aButSize ); |
149 | rButtonViewData.SetScenButSize( aButSize ); |
150 | |
151 | long nTextStart = bLayoutRTL ? aInner.Right() - aTextSize.Width() + 1 : aInner.Left(); |
152 | |
153 | bool bWasClip = false; |
154 | vcl::Region aOldClip; |
155 | bool bClip = ( aTextSize.Width() > aOuter.Right() - nBWidth - aInner.Left() ); |
156 | if ( bClip ) |
157 | { |
158 | if (pDev->IsClipRegion()) |
159 | { |
160 | bWasClip = true; |
161 | aOldClip = pDev->GetActiveClipRegion(); |
162 | } |
163 | long nClipStartX = bLayoutRTL ? aOuter.Left() + nBWidth : aInner.Left(); |
164 | long nClipEndX = bLayoutRTL ? aInner.Right() : aOuter.Right() - nBWidth; |
165 | pDev->SetClipRegion( vcl::Region(tools::Rectangle( nClipStartX, nButtonY + nVer/2, |
166 | nClipEndX, nButtonY + nVer/2 + aTextSize.Height())) ); |
167 | } |
168 | |
169 | pDev->DrawText( Point( nTextStart, nButtonY + nVer/2 ), rTitle ); |
170 | |
171 | if ( bClip ) |
172 | { |
173 | if ( bWasClip ) |
174 | pDev->SetClipRegion(aOldClip); |
175 | else |
176 | pDev->SetClipRegion(); |
177 | } |
178 | |
179 | pDev->SetFillColor(); |
180 | pDev->SetLineColor( COL_BLACK ); |
181 | pDev->DrawRect( aInner ); |
182 | pDev->DrawRect( aOuter ); |
183 | } |
184 | |
185 | static void lcl_DrawScenarioFrames( OutputDevice* pDev, ScViewData& rViewData, ScSplitPos eWhich, |
186 | SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 ) |
187 | { |
188 | ScDocument& rDoc = rViewData.GetDocument(); |
189 | SCTAB nTab = rViewData.GetTabNo(); |
190 | SCTAB nTabCount = rDoc.GetTableCount(); |
191 | if ( nTab+1 >= nTabCount || !rDoc.IsScenario(nTab+1) || rDoc.IsScenario(nTab) ) |
192 | return; |
193 | |
194 | if ( nX1 > 0 ) --nX1; |
195 | if ( nY1>=2 ) nY1 -= 2; // Hack: Header row affects two cells |
196 | else if ( nY1 > 0 ) --nY1; |
197 | if ( nX2 < rDoc.MaxCol() ) ++nX2; |
198 | if ( nY2 < rDoc.MaxRow()-1 ) nY2 += 2; // Hack: Header row affects two cells |
199 | else if ( nY2 < rDoc.MaxRow() ) ++nY2; |
200 | ScRange aViewRange( nX1,nY1,nTab, nX2,nY2,nTab ); |
201 | |
202 | //! cache the ranges in table!!!! |
203 | |
204 | ScMarkData aMarks(rDoc.GetSheetLimits()); |
205 | for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++) |
206 | rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame ); |
207 | ScRangeListRef xRanges = new ScRangeList; |
208 | aMarks.FillRangeListWithMarks( xRanges.get(), false ); |
209 | |
210 | bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); |
211 | long nLayoutSign = bLayoutRTL ? -1 : 1; |
212 | |
213 | for (size_t j = 0, n = xRanges->size(); j < n; ++j) |
214 | { |
215 | ScRange aRange = (*xRanges)[j]; |
216 | // Always extend scenario frame to merged cells where no new non-covered cells |
217 | // are framed |
218 | rDoc.ExtendTotalMerge( aRange ); |
219 | |
220 | //! -> Extend repaint when merging !!! |
221 | |
222 | if ( aRange.Intersects( aViewRange ) ) //! Space for Text/Button? |
223 | { |
224 | Point aStartPos = rViewData.GetScrPos( |
225 | aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true ); |
226 | Point aEndPos = rViewData.GetScrPos( |
227 | aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich, true ); |
228 | // on the grid: |
229 | aStartPos.AdjustX( -nLayoutSign ); |
230 | aStartPos.AdjustY( -1 ); |
231 | aEndPos.AdjustX( -nLayoutSign ); |
232 | aEndPos.AdjustY( -1 ); |
233 | |
234 | bool bTextBelow = ( aRange.aStart.Row() == 0 ); |
235 | |
236 | OUString aCurrent; |
237 | Color aColor( COL_LIGHTGRAY ); |
238 | for (SCTAB nAct=nTab+1; nAct<nTabCount && rDoc.IsScenario(nAct); nAct++) |
239 | if ( rDoc.IsActiveScenario(nAct) && rDoc.HasScenarioRange(nAct,aRange) ) |
240 | { |
241 | OUString aDummyComment; |
242 | ScScenarioFlags nDummyFlags; |
243 | rDoc.GetName( nAct, aCurrent ); |
244 | rDoc.GetScenarioData( nAct, aDummyComment, aColor, nDummyFlags ); |
245 | } |
246 | |
247 | if (aCurrent.isEmpty()) |
248 | aCurrent = ScResId( STR_EMPTYDATAreinterpret_cast<char const *>("STR_EMPTYDATA" "\004" u8"(empty)" ) ); |
249 | |
250 | //! Own text "(None)" instead of "(Empty)" ??? |
251 | |
252 | lcl_DrawOneFrame( pDev, tools::Rectangle( aStartPos, aEndPos ), |
253 | aCurrent, aColor, bTextBelow, |
254 | rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomY(), |
255 | rDoc, rViewData, bLayoutRTL ); |
256 | } |
257 | } |
258 | } |
259 | |
260 | static void lcl_DrawHighlight( ScOutputData& rOutputData, const ScViewData& rViewData, |
261 | const std::vector<ScHighlightEntry>& rHighlightRanges ) |
262 | { |
263 | SCTAB nTab = rViewData.GetTabNo(); |
264 | for ( const auto& rHighlightRange : rHighlightRanges) |
265 | { |
266 | ScRange aRange = rHighlightRange.aRef; |
267 | if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() ) |
268 | { |
269 | rOutputData.DrawRefMark( |
270 | aRange.aStart.Col(), aRange.aStart.Row(), |
271 | aRange.aEnd.Col(), aRange.aEnd.Row(), |
272 | rHighlightRange.aColor, false ); |
273 | } |
274 | } |
275 | } |
276 | |
277 | void ScGridWindow::DoInvertRect( const tools::Rectangle& rPixel ) |
278 | { |
279 | if ( rPixel == aInvertRect ) |
280 | aInvertRect = tools::Rectangle(); // Cancel |
281 | else |
282 | { |
283 | OSL_ENSURE( aInvertRect.IsEmpty(), "DoInvertRect no pairs" )do { if (true && (!(aInvertRect.IsEmpty()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx" ":" "283" ": "), "%s", "DoInvertRect no pairs"); } } while ( false); |
284 | |
285 | aInvertRect = rPixel; // Mark new rectangle |
286 | } |
287 | |
288 | UpdateHeaderOverlay(); // uses aInvertRect |
289 | } |
290 | |
291 | void ScGridWindow::PrePaint(vcl::RenderContext& /*rRenderContext*/) |
292 | { |
293 | // forward PrePaint to DrawingLayer |
294 | ScTabViewShell* pTabViewShell = mrViewData.GetViewShell(); |
295 | |
296 | if(pTabViewShell) |
297 | { |
298 | SdrView* pDrawView = pTabViewShell->GetScDrawView(); |
299 | |
300 | if (pDrawView) |
301 | { |
302 | pDrawView->PrePaint(); |
303 | } |
304 | } |
305 | } |
306 | |
307 | bool ScGridWindow::NeedLOKCursorInvalidation(const tools::Rectangle& rCursorRect, |
308 | const Fraction aScaleX, const Fraction aScaleY) |
309 | { |
310 | // Don't see the need for a map as there will be only a few zoom levels |
311 | // and as of now X and Y zooms in online are the same. |
312 | for (auto& rEntry : maLOKLastCursor) |
313 | { |
314 | if (aScaleX == rEntry.aScaleX && aScaleY == rEntry.aScaleY) |
315 | { |
316 | if (rCursorRect == rEntry.aRect) |
317 | return false; // No change |
318 | |
319 | // Update and allow invalidate. |
320 | rEntry.aRect = rCursorRect; |
321 | return true; |
322 | } |
323 | } |
324 | |
325 | maLOKLastCursor.push_back(LOKCursorEntry{aScaleX, aScaleY, rCursorRect}); |
326 | return true; |
327 | } |
328 | |
329 | void ScGridWindow::InvalidateLOKViewCursor(const tools::Rectangle& rCursorRect, |
330 | const Fraction aScaleX, const Fraction aScaleY) |
331 | { |
332 | if (!NeedLOKCursorInvalidation(rCursorRect, aScaleX, aScaleY)) |
333 | return; |
334 | |
335 | ScTabViewShell* pThisViewShell = mrViewData.GetViewShell(); |
336 | SfxViewShell* pViewShell = SfxViewShell::GetFirst(); |
337 | |
338 | while (pViewShell) |
339 | { |
340 | if (pViewShell != pThisViewShell && pViewShell->GetDocId() == pThisViewShell->GetDocId()) |
341 | { |
342 | ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell); |
343 | if (pOtherViewShell) |
344 | { |
345 | ScViewData& rOtherViewData = pOtherViewShell->GetViewData(); |
346 | Fraction aZoomX = rOtherViewData.GetZoomX(); |
347 | Fraction aZoomY = rOtherViewData.GetZoomY(); |
348 | if (aZoomX == aScaleX && aZoomY == aScaleY) |
349 | { |
350 | SfxLokHelper::notifyOtherView(pThisViewShell, pOtherViewShell, |
351 | LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", rCursorRect.toString()); |
352 | } |
353 | } |
354 | } |
355 | |
356 | pViewShell = SfxViewShell::GetNext(*pViewShell); |
357 | } |
358 | } |
359 | |
360 | void ScGridWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect ) |
361 | { |
362 | ScDocument& rDoc = mrViewData.GetDocument(); |
363 | if ( rDoc.IsInInterpreter() ) |
364 | { |
365 | // Via Reschedule, interpreted cells do not trigger Invalidate again, |
366 | // otherwise for instance an error box would never appear (bug 36381). |
367 | // Later, through bNeedsRepaint everything is painted again. |
368 | if ( bNeedsRepaint ) |
369 | { |
370 | //! Merge Rectangle? |
371 | aRepaintPixel = tools::Rectangle(); // multiple -> paint all |
372 | } |
373 | else |
374 | { |
375 | bNeedsRepaint = true; |
376 | aRepaintPixel = LogicToPixel(rRect); // only affected ranges |
377 | } |
378 | return; |
379 | } |
380 | |
381 | // #i117893# If GetSizePixel needs to call the resize handler, the resulting nested Paint call |
382 | // (possibly for a larger rectangle) has to be allowed. Call GetSizePixel before setting bIsInPaint. |
383 | GetSizePixel(); |
384 | |
385 | if (bIsInPaint) |
386 | return; |
387 | |
388 | bIsInPaint = true; |
389 | |
390 | tools::Rectangle aPixRect = LogicToPixel( rRect ); |
391 | |
392 | SCCOL nX1 = mrViewData.GetPosX(eHWhich); |
393 | SCROW nY1 = mrViewData.GetPosY(eVWhich); |
394 | |
395 | SCTAB nTab = mrViewData.GetTabNo(); |
396 | |
397 | double nPPTX = mrViewData.GetPPTX(); |
398 | double nPPTY = mrViewData.GetPPTY(); |
399 | |
400 | tools::Rectangle aMirroredPixel = aPixRect; |
401 | if ( rDoc.IsLayoutRTL( nTab ) ) |
402 | { |
403 | // mirror and swap |
404 | long nWidth = GetSizePixel().Width(); |
405 | aMirroredPixel.SetLeft( nWidth - 1 - aPixRect.Right() ); |
406 | aMirroredPixel.SetRight( nWidth - 1 - aPixRect.Left() ); |
407 | } |
408 | |
409 | long nScrX = ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX ); |
410 | while ( nScrX <= aMirroredPixel.Left() && nX1 < rDoc.MaxCol() ) |
411 | { |
412 | ++nX1; |
413 | nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX ); |
414 | } |
415 | SCCOL nX2 = nX1; |
416 | while ( nScrX <= aMirroredPixel.Right() && nX2 < rDoc.MaxCol() ) |
417 | { |
418 | ++nX2; |
419 | nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX2, nTab ), nPPTX ); |
420 | } |
421 | |
422 | long nScrY = 0; |
423 | ScViewData::AddPixelsWhile( nScrY, aPixRect.Top(), nY1, rDoc.MaxRow(), nPPTY, &rDoc, nTab); |
424 | SCROW nY2 = nY1; |
425 | if (nScrY <= aPixRect.Bottom() && nY2 < rDoc.MaxRow()) |
426 | { |
427 | ++nY2; |
428 | ScViewData::AddPixelsWhile( nScrY, aPixRect.Bottom(), nY2, rDoc.MaxRow(), nPPTY, &rDoc, nTab); |
429 | } |
430 | |
431 | Draw( nX1,nY1,nX2,nY2, ScUpdateMode::Marks ); // don't continue with painting |
432 | |
433 | bIsInPaint = false; |
434 | } |
435 | |
436 | void ScGridWindow::Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, ScUpdateMode eMode ) |
437 | { |
438 | ScDocument& rDoc = mrViewData.GetDocument(); |
439 | |
440 | // let's ignore the normal Draw() attempts when doing the tiled rendering, |
441 | // all the rendering should go through PaintTile() in that case. |
442 | // TODO revisit if we can actually turn this into an assert(), and clean |
443 | // up the callers |
444 | if (comphelper::LibreOfficeKit::isActive()) |
445 | return; |
446 | |
447 | ScModule* pScMod = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule ::Calc)) ); |
448 | bool bTextWysiwyg = pScMod->GetInputOptions().GetTextWysiwyg(); |
449 | |
450 | if (mrViewData.IsMinimized()) |
451 | return; |
452 | |
453 | PutInOrder( nX1, nX2 ); |
454 | PutInOrder( nY1, nY2 ); |
455 | |
456 | OSL_ENSURE( rDoc.ValidCol(nX2) && rDoc.ValidRow(nY2), "GridWin Draw area too big" )do { if (true && (!(rDoc.ValidCol(nX2) && rDoc .ValidRow(nY2)))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN ), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx" ":" "456" ": "), "%s", "GridWin Draw area too big"); } } while (false); |
457 | |
458 | UpdateVisibleRange(); |
459 | |
460 | if (nX2 < maVisibleRange.mnCol1 || nY2 < maVisibleRange.mnRow1) |
461 | return; |
462 | // invisible |
463 | if (nX1 < maVisibleRange.mnCol1) |
464 | nX1 = maVisibleRange.mnCol1; |
465 | if (nY1 < maVisibleRange.mnRow1) |
466 | nY1 = maVisibleRange.mnRow1; |
467 | |
468 | if (nX1 > maVisibleRange.mnCol2 || nY1 > maVisibleRange.mnRow2) |
469 | return; |
470 | |
471 | if (nX2 > maVisibleRange.mnCol2) |
472 | nX2 = maVisibleRange.mnCol2; |
473 | if (nY2 > maVisibleRange.mnRow2) |
474 | nY2 = maVisibleRange.mnRow2; |
475 | |
476 | if ( eMode != ScUpdateMode::Marks && nX2 < maVisibleRange.mnCol2) |
477 | nX2 = maVisibleRange.mnCol2; // to continue painting |
478 | |
479 | // point of no return |
480 | |
481 | ++nPaintCount; // mark that painting is in progress |
482 | |
483 | SCTAB nTab = mrViewData.GetTabNo(); |
484 | rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab ); |
485 | |
486 | Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich ); |
487 | long nMirrorWidth = GetSizePixel().Width(); |
488 | bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); |
489 | if ( bLayoutRTL ) |
490 | { |
491 | long nEndPixel = mrViewData.GetScrPos( nX2+1, maVisibleRange.mnRow1, eWhich ).X(); |
492 | nMirrorWidth = aScrPos.X() - nEndPixel; |
493 | aScrPos.setX( nEndPixel + 1 ); |
494 | } |
495 | |
496 | long nScrX = aScrPos.X(); |
497 | long nScrY = aScrPos.Y(); |
498 | |
499 | SCCOL nCurX = mrViewData.GetCurX(); |
500 | SCROW nCurY = mrViewData.GetCurY(); |
501 | SCCOL nCurEndX = nCurX; |
502 | SCROW nCurEndY = nCurY; |
503 | rDoc.ExtendMerge( nCurX, nCurY, nCurEndX, nCurEndY, nTab ); |
504 | bool bCurVis = nCursorHideCount==0 && |
505 | ( nCurEndX+1 >= nX1 && nCurX <= nX2+1 && nCurEndY+1 >= nY1 && nCurY <= nY2+1 ); |
506 | |
507 | // AutoFill Handles |
508 | if ( !bCurVis && nCursorHideCount==0 && bAutoMarkVisible && aAutoMarkPos.Tab() == nTab && |
509 | ( aAutoMarkPos.Col() != nCurX || aAutoMarkPos.Row() != nCurY ) ) |
510 | { |
511 | SCCOL nHdlX = aAutoMarkPos.Col(); |
512 | SCROW nHdlY = aAutoMarkPos.Row(); |
513 | rDoc.ExtendMerge( nHdlX, nHdlY, nHdlX, nHdlY, nTab ); |
514 | // left and top is unaffected |
515 | |
516 | //! Paint AutoFill handles alone (without Cursor) ??? |
517 | } |
518 | |
519 | double nPPTX = mrViewData.GetPPTX(); |
520 | double nPPTY = mrViewData.GetPPTY(); |
521 | |
522 | const ScViewOptions& rOpts = mrViewData.GetOptions(); |
523 | |
524 | // data block |
525 | |
526 | ScTableInfo aTabInfo; |
527 | rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab, |
528 | nPPTX, nPPTY, false, rOpts.GetOption(VOPT_FORMULAS), |
529 | &mrViewData.GetMarkData() ); |
530 | |
531 | Fraction aZoomX = mrViewData.GetZoomX(); |
532 | Fraction aZoomY = mrViewData.GetZoomY(); |
533 | ScOutputData aOutputData( this, OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab, |
534 | nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY, |
535 | &aZoomX, &aZoomY ); |
536 | |
537 | aOutputData.SetMirrorWidth( nMirrorWidth ); // needed for RTL |
538 | aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get()); |
539 | |
540 | ScopedVclPtr< VirtualDevice > xFmtVirtDev; |
541 | bool bLogicText = bTextWysiwyg; // call DrawStrings in logic MapMode? |
542 | |
543 | if ( bTextWysiwyg ) |
544 | { |
545 | // use printer for text formatting |
546 | |
547 | OutputDevice* pFmtDev = rDoc.GetPrinter(); |
548 | pFmtDev->SetMapMode( mrViewData.GetLogicMode(eWhich) ); |
549 | aOutputData.SetFmtDevice( pFmtDev ); |
550 | } |
551 | else if ( aZoomX != aZoomY && mrViewData.IsOle() ) |
552 | { |
553 | // #i45033# For OLE inplace editing with different zoom factors, |
554 | // use a virtual device with 1/100th mm as text formatting reference |
555 | |
556 | xFmtVirtDev.disposeAndReset( VclPtr<VirtualDevice>::Create() ); |
557 | xFmtVirtDev->SetMapMode(MapMode(MapUnit::Map100thMM)); |
558 | aOutputData.SetFmtDevice( xFmtVirtDev.get() ); |
559 | |
560 | bLogicText = true; // use logic MapMode |
561 | } |
562 | |
563 | DrawContent(*this, aTabInfo, aOutputData, bLogicText); |
564 | |
565 | // If something was inverted during the Paint (selection changed from Basic Macro) |
566 | // then this is now mixed up and has to be repainted |
567 | OSL_ENSURE(nPaintCount, "Wrong nPaintCount")do { if (true && (!(nPaintCount))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx" ":" "567" ": "), "%s", "Wrong nPaintCount"); } } while (false ); |
568 | --nPaintCount; |
569 | if (!nPaintCount) |
570 | CheckNeedsRepaint(); |
571 | |
572 | // Flag drawn formula cells "unchanged". |
573 | rDoc.ResetChanged(ScRange(nX1, nY1, nTab, nX2, nY2, nTab)); |
574 | rDoc.PrepareFormulaCalc(); |
575 | } |
576 | |
577 | namespace { |
578 | |
579 | class SuppressEditViewMessagesGuard |
580 | { |
581 | public: |
582 | SuppressEditViewMessagesGuard(EditView& rEditView) : |
583 | mrEditView(rEditView), |
584 | mbOrigSuppressFlag(rEditView.IsSuppressLOKMessages()) |
585 | { |
586 | if (!mbOrigSuppressFlag) |
587 | mrEditView.SuppressLOKMessages(true); |
588 | } |
589 | |
590 | ~SuppressEditViewMessagesGuard() |
591 | { |
592 | if (mrEditView.IsSuppressLOKMessages() != mbOrigSuppressFlag) |
593 | mrEditView.SuppressLOKMessages(mbOrigSuppressFlag); |
594 | } |
595 | |
596 | private: |
597 | EditView& mrEditView; |
598 | const bool mbOrigSuppressFlag; |
599 | }; |
600 | |
601 | } |
602 | |
603 | void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData, |
604 | bool bLogicText) |
605 | { |
606 | ScModule* pScMod = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule ::Calc)) ); |
607 | ScDocument& rDoc = mrViewData.GetDocument(); |
608 | const ScViewOptions& rOpts = mrViewData.GetOptions(); |
609 | bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive(); |
610 | bool bNoBackgroundAndGrid = bIsTiledRendering |
611 | && comphelper::LibreOfficeKit::isCompatFlagSet( |
612 | comphelper::LibreOfficeKit::Compat::scNoGridBackground); |
613 | |
614 | SCTAB nTab = aOutputData.nTab; |
615 | SCCOL nX1 = aOutputData.nX1; |
616 | SCROW nY1 = aOutputData.nY1; |
617 | SCCOL nX2 = aOutputData.nX2; |
618 | SCROW nY2 = aOutputData.nY2; |
619 | long nScrX = aOutputData.nScrX; |
620 | long nScrY = aOutputData.nScrY; |
621 | |
622 | const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig(); |
623 | Color aGridColor( rColorCfg.GetColorValue( svtools::CALCGRID, false ).nColor ); |
624 | if ( aGridColor == COL_TRANSPARENT ) |
625 | { |
626 | // use view options' grid color only if color config has "automatic" color |
627 | aGridColor = rOpts.GetGridColor(); |
628 | } |
629 | |
630 | aOutputData.SetSyntaxMode ( mrViewData.IsSyntaxMode() ); |
631 | aOutputData.SetGridColor ( aGridColor ); |
632 | aOutputData.SetShowNullValues ( rOpts.GetOption( VOPT_NULLVALS ) ); |
633 | aOutputData.SetShowFormulas ( rOpts.GetOption( VOPT_FORMULAS ) ); |
634 | aOutputData.SetShowSpellErrors ( rDoc.GetDocOptions().IsAutoSpell() ); |
635 | aOutputData.SetMarkClipped ( rOpts.GetOption( VOPT_CLIPMARKS ) ); |
636 | |
637 | aOutputData.SetUseStyleColor( true ); // always set in table view |
638 | |
639 | aOutputData.SetViewShell( mrViewData.GetViewShell() ); |
640 | |
641 | bool bGrid = rOpts.GetOption( VOPT_GRID ) && mrViewData.GetShowGrid(); |
642 | bool bGridFirst = !rOpts.GetOption( VOPT_GRID_ONTOP ); |
643 | |
644 | bool bPage = rOpts.GetOption( VOPT_PAGEBREAKS ) && !bIsTiledRendering; |
645 | |
646 | bool bPageMode = mrViewData.IsPagebreakMode(); |
647 | if (bPageMode) // after FindChanged |
648 | { |
649 | // SetPagebreakMode also initializes bPrinted Flags |
650 | aOutputData.SetPagebreakMode( mrViewData.GetView()->GetPageBreakData() ); |
651 | } |
652 | |
653 | EditView* pEditView = nullptr; |
654 | bool bEditMode = mrViewData.HasEditView(eWhich); |
655 | if ( bEditMode && mrViewData.GetRefTabNo() == nTab ) |
656 | { |
657 | SCCOL nEditCol; |
658 | SCROW nEditRow; |
659 | mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow ); |
660 | SCCOL nEditEndCol = mrViewData.GetEditEndCol(); |
661 | SCROW nEditEndRow = mrViewData.GetEditEndRow(); |
662 | |
663 | if ( nEditEndCol >= nX1 && nEditCol <= nX2 && nEditEndRow >= nY1 && nEditRow <= nY2 ) |
664 | aOutputData.SetEditCell( nEditCol, nEditRow ); |
665 | else |
666 | bEditMode = false; |
667 | } |
668 | |
669 | const MapMode aOriginalMode = rDevice.GetMapMode(); |
670 | |
671 | // define drawing layer map mode and paint rectangle |
672 | MapMode aDrawMode = GetDrawMapMode(); |
673 | if (bIsTiledRendering) |
674 | { |
675 | // FIXME this shouldn't be necessary once we change the entire Calc to |
676 | // work in the logic coordinates (ideally 100ths of mm - so that it is |
677 | // the same as editeng and drawinglayer), and get rid of all the |
678 | // SetMapMode's and other unnecessary fun we have with pixels |
679 | // See also ScGridWindow::GetDrawMapMode() for the rest of this hack |
680 | aDrawMode.SetOrigin(PixelToLogic(Point(nScrX, nScrY), aDrawMode)); |
681 | } |
682 | tools::Rectangle aDrawingRectLogic; |
683 | bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); |
684 | |
685 | { |
686 | // get drawing pixel rect |
687 | tools::Rectangle aDrawingRectPixel(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH())); |
688 | |
689 | // correct for border (left/right) |
690 | if(rDoc.MaxCol() == nX2) |
691 | { |
692 | if(bLayoutRTL) |
693 | { |
694 | aDrawingRectPixel.SetLeft( 0 ); |
695 | } |
696 | else |
697 | { |
698 | aDrawingRectPixel.SetRight( GetOutputSizePixel().getWidth() ); |
699 | } |
700 | } |
701 | |
702 | // correct for border (bottom) |
703 | if(rDoc.MaxRow() == nY2) |
704 | { |
705 | aDrawingRectPixel.SetBottom( GetOutputSizePixel().getHeight() ); |
706 | } |
707 | |
708 | // get logic positions |
709 | aDrawingRectLogic = PixelToLogic(aDrawingRectPixel, aDrawMode); |
710 | } |
711 | |
712 | OutputDevice* pContentDev = &rDevice; // device for document content, used by overlay manager |
713 | SdrPaintWindow* pTargetPaintWindow = nullptr; // #i74769# work with SdrPaintWindow directly |
714 | |
715 | { |
716 | // init redraw |
717 | ScTabViewShell* pTabViewShell = mrViewData.GetViewShell(); |
718 | |
719 | if(pTabViewShell) |
720 | { |
721 | MapMode aCurrentMapMode(pContentDev->GetMapMode()); |
722 | pContentDev->SetMapMode(aDrawMode); |
723 | SdrView* pDrawView = pTabViewShell->GetScDrawView(); |
724 | |
725 | if(pDrawView) |
726 | { |
727 | // #i74769# Use new BeginDrawLayers() interface |
728 | vcl::Region aDrawingRegion(aDrawingRectLogic); |
729 | pTargetPaintWindow = pDrawView->BeginDrawLayers(pContentDev, aDrawingRegion); |
730 | OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)")do { if (true && (!(pTargetPaintWindow))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx" ":" "730" ": "), "%s", "BeginDrawLayers: Got no SdrPaintWindow (!)" ); } } while (false); |
731 | |
732 | if (!bIsTiledRendering) |
733 | { |
734 | // #i74769# get target device from SdrPaintWindow, this may be the prerender |
735 | // device now, too. |
736 | pContentDev = &(pTargetPaintWindow->GetTargetOutputDevice()); |
737 | aOutputData.SetContentDevice(pContentDev); |
738 | } |
739 | } |
740 | |
741 | pContentDev->SetMapMode(aCurrentMapMode); |
742 | } |
743 | } |
744 | |
745 | // edge (area) (Pixel) |
746 | if ( nX2==rDoc.MaxCol() || nY2==rDoc.MaxRow() ) |
747 | { |
748 | // save MapMode and set to pixel |
749 | MapMode aCurrentMapMode(pContentDev->GetMapMode()); |
750 | pContentDev->SetMapMode(MapMode(MapUnit::MapPixel)); |
751 | |
752 | tools::Rectangle aPixRect( Point(), GetOutputSizePixel() ); |
753 | pContentDev->SetFillColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor ); |
754 | pContentDev->SetLineColor(); |
755 | if ( nX2==rDoc.MaxCol() ) |
756 | { |
757 | tools::Rectangle aDrawRect( aPixRect ); |
758 | if ( bLayoutRTL ) |
759 | aDrawRect.SetRight( nScrX - 1 ); |
760 | else |
761 | aDrawRect.SetLeft( nScrX + aOutputData.GetScrW() ); |
762 | if (aDrawRect.Right() >= aDrawRect.Left()) |
763 | pContentDev->DrawRect( aDrawRect ); |
764 | } |
765 | if ( nY2==rDoc.MaxRow() ) |
766 | { |
767 | tools::Rectangle aDrawRect( aPixRect ); |
768 | aDrawRect.SetTop( nScrY + aOutputData.GetScrH() ); |
769 | if ( nX2==rDoc.MaxCol() ) |
770 | { |
771 | // no double painting of the corner |
772 | if ( bLayoutRTL ) |
773 | aDrawRect.SetLeft( nScrX ); |
774 | else |
775 | aDrawRect.SetRight( nScrX + aOutputData.GetScrW() - 1 ); |
776 | } |
777 | if (aDrawRect.Bottom() >= aDrawRect.Top()) |
778 | pContentDev->DrawRect( aDrawRect ); |
779 | } |
780 | |
781 | // restore MapMode |
782 | pContentDev->SetMapMode(aCurrentMapMode); |
783 | } |
784 | |
785 | if ( rDoc.HasBackgroundDraw( nTab, aDrawingRectLogic ) ) |
786 | { |
787 | pContentDev->SetMapMode(MapMode(MapUnit::MapPixel)); |
788 | aOutputData.DrawClear(); |
789 | |
790 | // drawing background |
791 | |
792 | pContentDev->SetMapMode(aDrawMode); |
793 | DrawRedraw( aOutputData, SC_LAYER_BACK ); |
794 | } |
795 | else |
796 | aOutputData.SetSolidBackground(!bNoBackgroundAndGrid); |
797 | |
798 | aOutputData.DrawDocumentBackground(); |
799 | |
800 | if (bGridFirst && (bGrid || bPage) && !bNoBackgroundAndGrid) |
801 | aOutputData.DrawGrid(*pContentDev, bGrid, bPage); |
802 | |
803 | aOutputData.DrawBackground(*pContentDev); |
804 | |
805 | if (!bGridFirst && (bGrid || bPage) && !bNoBackgroundAndGrid) |
806 | aOutputData.DrawGrid(*pContentDev, bGrid, bPage); |
807 | |
808 | pContentDev->SetMapMode(MapMode(MapUnit::MapPixel)); |
809 | |
810 | if ( bPageMode ) |
811 | { |
812 | // DrawPagePreview draws complete lines/page numbers, must always be clipped |
813 | if ( aOutputData.SetChangedClip() ) |
814 | { |
815 | DrawPagePreview(nX1,nY1,nX2,nY2, *pContentDev); |
816 | pContentDev->SetClipRegion(); |
817 | } |
818 | } |
819 | |
820 | aOutputData.DrawShadow(); |
821 | aOutputData.DrawFrame(*pContentDev); |
822 | |
823 | // Show Note Mark |
824 | if ( rOpts.GetOption( VOPT_NOTES ) ) |
825 | aOutputData.DrawNoteMarks(*pContentDev); |
826 | |
827 | if ( !bLogicText ) |
828 | aOutputData.DrawStrings(); // in pixel MapMode |
829 | |
830 | // edit cells and printer-metrics text must be before the buttons |
831 | // (DataPilot buttons contain labels in UI font) |
832 | |
833 | pContentDev->SetMapMode(mrViewData.GetLogicMode(eWhich)); |
834 | if ( bLogicText ) |
835 | aOutputData.DrawStrings(true); // in logic MapMode if bLogicText is set |
836 | aOutputData.DrawEdit(true); |
837 | |
838 | // the buttons are painted in absolute coordinates |
839 | if (bIsTiledRendering) |
840 | { |
841 | // Tiled offset nScrX, nScrY |
842 | MapMode aMap( MapUnit::MapPixel ); |
843 | Point aOrigin = aOriginalMode.GetOrigin(); |
844 | aOrigin.setX(aOrigin.getX() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScrX); |
845 | aOrigin.setY(aOrigin.getY() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScrY); |
846 | aMap.SetOrigin(aOrigin); |
847 | pContentDev->SetMapMode(aMap); |
848 | } |
849 | else |
850 | pContentDev->SetMapMode(MapMode(MapUnit::MapPixel)); |
851 | |
852 | // Autofilter- and Pivot-Buttons |
853 | |
854 | DrawButtons(nX1, nX2, rTableInfo, pContentDev); // Pixel |
855 | |
856 | pContentDev->SetMapMode(MapMode(MapUnit::MapPixel)); |
857 | |
858 | aOutputData.DrawClipMarks(); |
859 | |
860 | // In any case, Scenario / ChangeTracking must happen after DrawGrid, also for !bGridFirst |
861 | |
862 | //! test if ChangeTrack display is active |
863 | //! Disable scenario frame via view option? |
864 | |
865 | SCTAB nTabCount = rDoc.GetTableCount(); |
866 | const std::vector<ScHighlightEntry> &rHigh = mrViewData.GetView()->GetHighlightRanges(); |
867 | bool bHasScenario = ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) ); |
868 | bool bHasChange = ( rDoc.GetChangeTrack() != nullptr ); |
869 | |
870 | if ( bHasChange || bHasScenario || !rHigh.empty() ) |
871 | { |
872 | //! Merge SetChangedClip() with DrawMarks() ?? (different MapMode!) |
873 | |
874 | if ( bHasChange ) |
875 | aOutputData.DrawChangeTrack(); |
876 | |
877 | if ( bHasScenario ) |
878 | lcl_DrawScenarioFrames( pContentDev, mrViewData, eWhich, nX1,nY1,nX2,nY2 ); |
879 | |
880 | lcl_DrawHighlight( aOutputData, mrViewData, rHigh ); |
881 | } |
882 | |
883 | // Drawing foreground |
884 | |
885 | pContentDev->SetMapMode(aDrawMode); |
886 | |
887 | // Bitmaps and buttons are in absolute pixel coordinates. |
888 | const MapMode aOrig = pContentDev->GetMapMode(); |
889 | if (bIsTiledRendering) |
890 | { |
891 | Point aOrigin = aOriginalMode.GetOrigin(); |
892 | Size aPixelOffset(aOrigin.getX() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0), aOrigin.getY() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0)); |
893 | pContentDev->SetPixelOffset(aPixelOffset); |
894 | comphelper::LibreOfficeKit::setLocalRendering(); |
895 | } |
896 | |
897 | DrawRedraw( aOutputData, SC_LAYER_FRONT ); |
898 | DrawRedraw( aOutputData, SC_LAYER_INTERN ); |
899 | DrawSdrGrid( aDrawingRectLogic, pContentDev ); |
900 | |
901 | if (bIsTiledRendering) |
902 | { |
903 | pContentDev->SetPixelOffset(Size()); |
904 | pContentDev->SetMapMode(aOrig); |
905 | } |
906 | |
907 | pContentDev->SetMapMode(MapMode(MapUnit::MapPixel)); |
908 | |
909 | if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() ) |
910 | { |
911 | Color aRefColor( rColorCfg.GetColorValue(svtools::CALCREFERENCE).nColor ); |
912 | aOutputData.DrawRefMark( mrViewData.GetRefStartX(), mrViewData.GetRefStartY(), |
913 | mrViewData.GetRefEndX(), mrViewData.GetRefEndY(), |
914 | aRefColor, false ); |
915 | } |
916 | |
917 | // range finder |
918 | |
919 | ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() ); |
920 | if (pHdl) |
921 | { |
922 | ScDocShell* pDocSh = mrViewData.GetDocShell(); |
923 | ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList(); |
924 | if ( pRangeFinder && !pRangeFinder->IsHidden() && |
925 | pRangeFinder->GetDocName() == pDocSh->GetTitle() ) |
926 | { |
927 | sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count()); |
928 | for (sal_uInt16 i=0; i<nCount; i++) |
929 | { |
930 | ScRangeFindData& rData = pRangeFinder->GetObject(i); |
931 | |
932 | ScRange aRef = rData.aRef; |
933 | aRef.PutInOrder(); |
934 | if ( aRef.aStart.Tab() >= nTab && aRef.aEnd.Tab() <= nTab ) |
935 | aOutputData.DrawRefMark( aRef.aStart.Col(), aRef.aStart.Row(), |
936 | aRef.aEnd.Col(), aRef.aEnd.Row(), |
937 | rData.nColor, |
938 | true ); |
939 | } |
940 | } |
941 | } |
942 | |
943 | { |
944 | // end redraw |
945 | ScTabViewShell* pTabViewShell = mrViewData.GetViewShell(); |
946 | |
947 | if(pTabViewShell) |
948 | { |
949 | MapMode aCurrentMapMode(pContentDev->GetMapMode()); |
950 | pContentDev->SetMapMode(aDrawMode); |
951 | |
952 | if (bIsTiledRendering) |
953 | { |
954 | Point aOrigin = aOriginalMode.GetOrigin(); |
955 | aOrigin.setX(aOrigin.getX() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + aOutputData.nScrX); |
956 | aOrigin.setY(aOrigin.getY() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + aOutputData.nScrY); |
957 | const double twipFactor = 15 * 1.76388889; // 26.45833335 |
958 | aOrigin = Point(aOrigin.getX() * twipFactor, |
959 | aOrigin.getY() * twipFactor); |
960 | MapMode aNew = rDevice.GetMapMode(); |
961 | aNew.SetOrigin(aOrigin); |
962 | rDevice.SetMapMode(aNew); |
963 | } |
964 | |
965 | SdrView* pDrawView = pTabViewShell->GetScDrawView(); |
966 | |
967 | if(pDrawView) |
968 | { |
969 | // #i74769# work with SdrPaintWindow directly |
970 | pDrawView->EndDrawLayers(*pTargetPaintWindow, true); |
971 | } |
972 | |
973 | pContentDev->SetMapMode(aCurrentMapMode); |
974 | } |
975 | } |
976 | |
977 | // paint in-place editing on other views |
978 | if (bIsTiledRendering) |
979 | { |
980 | ScTabViewShell* pThisViewShell = mrViewData.GetViewShell(); |
981 | SfxViewShell* pViewShell = SfxViewShell::GetFirst(); |
982 | |
983 | while (pViewShell) |
984 | { |
985 | if (pViewShell != pThisViewShell && pViewShell->GetDocId() == pThisViewShell->GetDocId()) |
986 | { |
987 | ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell); |
988 | if (pTabViewShell) |
989 | { |
990 | ScViewData& rOtherViewData = pTabViewShell->GetViewData(); |
991 | ScSplitPos eOtherWhich = rOtherViewData.GetEditActivePart(); |
992 | |
993 | bool bOtherEditMode = rOtherViewData.HasEditView(eOtherWhich); |
994 | SCCOL nCol1 = rOtherViewData.GetEditStartCol(); |
995 | SCROW nRow1 = rOtherViewData.GetEditStartRow(); |
996 | SCCOL nCol2 = rOtherViewData.GetEditEndCol(); |
997 | SCROW nRow2 = rOtherViewData.GetEditEndRow(); |
998 | bOtherEditMode = bOtherEditMode |
999 | && ( nCol2 >= nX1 && nCol1 <= nX2 && nRow2 >= nY1 && nRow1 <= nY2 ); |
1000 | if (bOtherEditMode && rOtherViewData.GetRefTabNo() == nTab) |
1001 | { |
1002 | EditView* pOtherEditView = rOtherViewData.GetEditView(eOtherWhich); |
1003 | if (pOtherEditView) |
1004 | { |
1005 | long nScreenX = aOutputData.nScrX; |
1006 | long nScreenY = aOutputData.nScrY; |
1007 | |
1008 | rDevice.SetLineColor(); |
1009 | rDevice.SetFillColor(pOtherEditView->GetBackgroundColor()); |
1010 | Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eOtherWhich ); |
1011 | Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eOtherWhich ); |
1012 | |
1013 | // don't overwrite grid |
1014 | long nLayoutSign = bLayoutRTL ? -1 : 1; |
1015 | aEnd.AdjustX( -(2 * nLayoutSign) ); |
1016 | aEnd.AdjustY( -2 ); |
1017 | |
1018 | tools::Rectangle aBackground(aStart, aEnd); |
1019 | |
1020 | // Need to draw the background in absolute coords. |
1021 | Point aOrigin = aOriginalMode.GetOrigin(); |
1022 | aOrigin.setX(aOrigin.getX() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScreenX); |
1023 | aOrigin.setY(aOrigin.getY() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScreenY); |
1024 | aBackground += aOrigin; |
1025 | rDevice.SetMapMode(aDrawMode); |
1026 | |
1027 | static const double twipFactor = 15 * 1.76388889; // 26.45833335 |
1028 | // keep into account the zoom factor |
1029 | aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()), |
1030 | (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY())); |
1031 | |
1032 | MapMode aNew = rDevice.GetMapMode(); |
1033 | aNew.SetOrigin(aOrigin); |
1034 | rDevice.SetMapMode(aNew); |
1035 | |
1036 | // paint the background |
1037 | rDevice.DrawRect(rDevice.PixelToLogic(aBackground)); |
1038 | |
1039 | tools::Rectangle aEditRect(aBackground); |
1040 | aEditRect.AdjustLeft(1); |
1041 | aEditRect.AdjustTop(1); |
1042 | |
1043 | // EditView has an 'output area' which is used to clip the 'paint area' we provide below. |
1044 | // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin |
1045 | // attached to the EditView, so we have to change its mapmode too (temporarily). We save the |
1046 | // original mapmode and 'output area' and roll them back when we finish painting to rDevice. |
1047 | vcl::Window* pOtherWin = pOtherEditView->GetWindow(); |
1048 | const tools::Rectangle aOrigOutputArea(pOtherEditView->GetOutputArea()); // Not in pixels. |
1049 | const MapMode aOrigMapMode = pOtherWin->GetMapMode(); |
1050 | pOtherWin->SetMapMode(rDevice.GetMapMode()); |
1051 | |
1052 | // Avoid sending wrong cursor/selection messages by the 'other' view, as the output-area is going |
1053 | // to be tweaked temporarily to match the current view's zoom. |
1054 | SuppressEditViewMessagesGuard aGuard(*pOtherEditView); |
1055 | |
1056 | pOtherEditView->SetOutputArea(rDevice.PixelToLogic(aEditRect)); |
1057 | pOtherEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice); |
1058 | |
1059 | // Rollback the mapmode and 'output area'. |
1060 | pOtherWin->SetMapMode(aOrigMapMode); |
1061 | pOtherEditView->SetOutputArea(aOrigOutputArea); |
1062 | rDevice.SetMapMode(MapMode(MapUnit::MapPixel)); |
1063 | } |
1064 | } |
1065 | } |
1066 | } |
1067 | |
1068 | pViewShell = SfxViewShell::GetNext(*pViewShell); |
1069 | } |
1070 | |
1071 | } |
1072 | |
1073 | // In-place editing - when the user is typing, we need to paint the text |
1074 | // using the editeng. |
1075 | // It's being done after EndDrawLayers() to get it outside the overlay |
1076 | // buffer and on top of everything. |
1077 | if ( bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()) ) |
1078 | { |
1079 | // get the coordinates of the area we need to clear (overpaint by |
1080 | // the background) |
1081 | SCCOL nCol1 = mrViewData.GetEditStartCol(); |
1082 | SCROW nRow1 = mrViewData.GetEditStartRow(); |
1083 | SCCOL nCol2 = mrViewData.GetEditEndCol(); |
1084 | SCROW nRow2 = mrViewData.GetEditEndRow(); |
1085 | rDevice.SetLineColor(); |
1086 | rDevice.SetFillColor(pEditView->GetBackgroundColor()); |
1087 | Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eWhich ); |
1088 | Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eWhich ); |
1089 | |
1090 | // don't overwrite grid |
1091 | long nLayoutSign = bLayoutRTL ? -1 : 1; |
1092 | aEnd.AdjustX( -(2 * nLayoutSign) ); |
1093 | aEnd.AdjustY( -2 ); |
1094 | |
1095 | // toggle the cursor off if its on to ensure the cursor invert |
1096 | // background logic remains valid after the background is cleared on |
1097 | // the next cursor flash |
1098 | vcl::Cursor* pCrsr = pEditView->GetCursor(); |
1099 | const bool bVisCursor = pCrsr && pCrsr->IsVisible(); |
1100 | if (bVisCursor) |
1101 | pCrsr->Hide(); |
1102 | |
1103 | // set the correct mapmode |
1104 | tools::Rectangle aBackground(aStart, aEnd); |
1105 | tools::Rectangle aBGAbs(aStart, aEnd); |
1106 | |
1107 | if (bIsTiledRendering) |
1108 | { |
1109 | // Need to draw the background in absolute coords. |
1110 | Point aOrigin = aOriginalMode.GetOrigin(); |
1111 | aOrigin.setX(aOrigin.getX() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScrX); |
1112 | aOrigin.setY(aOrigin.getY() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScrY); |
1113 | aBackground += aOrigin; |
1114 | rDevice.SetMapMode(aDrawMode); |
1115 | } |
1116 | else |
1117 | rDevice.SetMapMode(mrViewData.GetLogicMode()); |
1118 | |
1119 | if (bIsTiledRendering) |
1120 | { |
1121 | Point aOrigin = aOriginalMode.GetOrigin(); |
1122 | aOrigin.setX(aOrigin.getX() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScrX); |
1123 | aOrigin.setY(aOrigin.getY() / TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0) + nScrY); |
1124 | static const double twipFactor = 15 * 1.76388889; // 26.45833335 |
1125 | // keep into account the zoom factor |
1126 | aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()), |
1127 | (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY())); |
1128 | MapMode aNew = rDevice.GetMapMode(); |
1129 | aNew.SetOrigin(aOrigin); |
1130 | rDevice.SetMapMode(aNew); |
1131 | } |
1132 | |
1133 | // paint the background |
1134 | tools::Rectangle aLogicRect(rDevice.PixelToLogic(aBackground)); |
1135 | //tdf#100925, rhbz#1283420, Draw some text here, to get |
1136 | //X11CairoTextRender::getCairoContext called, so that the forced read |
1137 | //from the underlying X Drawable gets it to sync. |
1138 | rDevice.DrawText(aLogicRect.BottomLeft(), " "); |
1139 | rDevice.DrawRect(aLogicRect); |
1140 | |
1141 | // paint the editeng text |
1142 | if (bIsTiledRendering) |
1143 | { |
1144 | tools::Rectangle aEditRect(aBackground); |
1145 | aEditRect.AdjustLeft(1); |
1146 | aEditRect.AdjustTop(1); |
1147 | // EditView has an 'output area' which is used to clip the paint area we provide below. |
1148 | // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin |
1149 | // attached to the EditView, so we have to change its mapmode too (temporarily). We save the |
1150 | // original mapmode and 'output area' and roll them back when we finish painting to rDevice. |
1151 | const tools::Rectangle aOrigOutputArea(pEditView->GetOutputArea()); // Not in pixels. |
1152 | const MapMode aOrigMapMode = GetMapMode(); |
1153 | SetMapMode(rDevice.GetMapMode()); |
1154 | |
1155 | // Avoid sending wrong cursor/selection messages by the current view, as the output-area is going |
1156 | // to be tweaked temporarily to match other view's zoom. (This does not affect the manual |
1157 | // cursor-messaging done in the non print-twips mode) |
1158 | SuppressEditViewMessagesGuard aGuard(*pEditView); |
1159 | |
1160 | pEditView->SetOutputArea(rDevice.PixelToLogic(aEditRect)); |
1161 | pEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice); |
1162 | |
1163 | // EditView will do the cursor notifications correctly if we're in |
1164 | // print-twips messaging mode. |
1165 | if (!comphelper::LibreOfficeKit::isCompatFlagSet( |
1166 | comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs)) |
1167 | { |
1168 | // Now we need to get relative cursor position within the editview. |
1169 | // This is for sending the pixel-aligned twips position of the cursor to the specific views with |
1170 | // the same given zoom level. |
1171 | tools::Rectangle aCursorRect = pEditView->GetEditCursor(); |
1172 | Point aCursPos = OutputDevice::LogicToLogic(aCursorRect.TopLeft(), |
1173 | MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip)); |
1174 | |
1175 | const MapMode& rDevMM = rDevice.GetMapMode(); |
1176 | MapMode aMM(MapUnit::MapTwip); |
1177 | aMM.SetScaleX(rDevMM.GetScaleX()); |
1178 | aMM.SetScaleY(rDevMM.GetScaleY()); |
1179 | |
1180 | aBGAbs.AdjustLeft(1); |
1181 | aBGAbs.AdjustTop(1); |
1182 | aCursorRect = OutputDevice::PixelToLogic(aBGAbs, aMM); |
1183 | aCursorRect.setWidth(0); |
1184 | aCursorRect.Move(aCursPos.getX(), 0); |
1185 | // Sends view cursor position to views of all matching zooms if needed (avoids duplicates). |
1186 | InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY()); |
1187 | } |
1188 | |
1189 | // Rollback the mapmode and 'output area'. |
1190 | SetMapMode(aOrigMapMode); |
1191 | pEditView->SetOutputArea(aOrigOutputArea); |
1192 | } |
1193 | else |
1194 | { |
1195 | tools::Rectangle aEditRect(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH())); |
1196 | pEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice); |
1197 | } |
1198 | |
1199 | rDevice.SetMapMode(MapMode(MapUnit::MapPixel)); |
1200 | |
1201 | // restore the cursor it was originally visible |
1202 | if (bVisCursor) |
1203 | pCrsr->Show(); |
1204 | } |
1205 | |
1206 | if (mrViewData.HasEditView(eWhich)) |
1207 | { |
1208 | // flush OverlayManager before changing the MapMode |
1209 | flushOverlayManager(); |
1210 | |
1211 | // set MapMode for text edit |
1212 | rDevice.SetMapMode(mrViewData.GetLogicMode()); |
1213 | } |
1214 | else |
1215 | rDevice.SetMapMode(aDrawMode); |
1216 | |
1217 | if (mpNoteMarker) |
1218 | mpNoteMarker->Draw(); // Above the cursor, in drawing map mode |
1219 | } |
1220 | |
1221 | namespace |
1222 | { |
1223 | template<typename IndexType> |
1224 | void lcl_getBoundingRowColumnforTile(ScViewData& rViewData, |
1225 | long nTileStartPosPx, long nTileEndPosPx, |
1226 | sal_Int32& nTopLeftTileOffset, sal_Int32& nTopLeftTileOrigin, |
1227 | sal_Int32& nTopLeftTileIndex, sal_Int32& nBottomRightTileIndex) |
1228 | { |
1229 | const bool bColumnHeader = std::is_same<IndexType, SCCOL>::value; |
1230 | |
1231 | SCTAB nTab = rViewData.GetTabNo(); |
1232 | |
1233 | IndexType nStartIndex = -1; |
1234 | IndexType nEndIndex = -1; |
1235 | long nStartPosPx = 0; |
1236 | long nEndPosPx = 0; |
1237 | |
1238 | ScPositionHelper& rPositionHelper = |
1239 | bColumnHeader ? rViewData.GetLOKWidthHelper() : rViewData.GetLOKHeightHelper(); |
1240 | const auto& rStartNearest = rPositionHelper.getNearestByPosition(nTileStartPosPx); |
1241 | const auto& rEndNearest = rPositionHelper.getNearestByPosition(nTileEndPosPx); |
1242 | |
1243 | ScBoundsProvider aBoundsProvider(rViewData, nTab, bColumnHeader); |
1244 | aBoundsProvider.Compute(rStartNearest, rEndNearest, nTileStartPosPx, nTileEndPosPx); |
1245 | aBoundsProvider.GetStartIndexAndPosition(nStartIndex, nStartPosPx); ++nStartIndex; |
1246 | aBoundsProvider.GetEndIndexAndPosition(nEndIndex, nEndPosPx); |
1247 | |
1248 | nTopLeftTileOffset = nTileStartPosPx - nStartPosPx; |
1249 | nTopLeftTileOrigin = nStartPosPx; |
1250 | nTopLeftTileIndex = nStartIndex; |
1251 | nBottomRightTileIndex = nEndIndex; |
1252 | } |
1253 | |
1254 | class ScLOKProxyObjectContact final : public sdr::contact::ObjectContactOfPageView |
1255 | { |
1256 | private: |
1257 | sdr::contact::ObjectContact& mrRealObjectContact; |
1258 | |
1259 | public: |
1260 | explicit ScLOKProxyObjectContact( |
1261 | sdr::contact::ObjectContact& rRealOC, |
1262 | SdrPageWindow& rPageWindow, |
1263 | const char* pDebugName) : |
1264 | ObjectContactOfPageView(rPageWindow, pDebugName), |
1265 | mrRealObjectContact(rRealOC) |
1266 | { |
1267 | } |
1268 | |
1269 | virtual bool supportsGridOffsets() const override { return true; } |
1270 | |
1271 | virtual void calculateGridOffsetForViewOjectContact( |
1272 | basegfx::B2DVector& rTarget, |
1273 | const sdr::contact::ViewObjectContact& rClient) const override |
1274 | { |
1275 | SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject()); |
1276 | if (pTargetSdrObject) |
1277 | rTarget = pTargetSdrObject->GetViewContact().GetViewObjectContact(mrRealObjectContact).getGridOffset(); |
1278 | } |
1279 | }; |
1280 | |
1281 | class ScLOKDrawView : public FmFormView |
1282 | { |
1283 | public: |
1284 | ScLOKDrawView(OutputDevice* pOut, ScViewData& rData) : |
1285 | FmFormView(*rData.GetDocument().GetDrawLayer(), pOut), |
1286 | pScDrawView(rData.GetScDrawView()) |
1287 | { |
1288 | } |
1289 | |
1290 | virtual sdr::contact::ObjectContact* createViewSpecificObjectContact( |
1291 | SdrPageWindow& rPageWindow, const char* pDebugName) const override |
1292 | { |
1293 | if (!pScDrawView) |
1294 | return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName); |
1295 | |
1296 | SdrPageView* pPageView(pScDrawView->GetSdrPageView()); |
1297 | if (!pPageView) |
1298 | return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName); |
1299 | |
1300 | SdrPageWindow* pSdrPageWindow = pPageView->GetPageWindow(0); |
1301 | if (!pSdrPageWindow) |
1302 | return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName); |
1303 | |
1304 | return new ScLOKProxyObjectContact(pSdrPageWindow->GetObjectContact(), rPageWindow, pDebugName); |
1305 | } |
1306 | |
1307 | private: |
1308 | ScDrawView* pScDrawView; |
1309 | }; |
1310 | } // anonymous namespace |
1311 | |
1312 | void ScGridWindow::PaintTile( VirtualDevice& rDevice, |
1313 | int nOutputWidth, int nOutputHeight, |
1314 | int nTilePosX, int nTilePosY, |
1315 | long nTileWidth, long nTileHeight ) |
1316 | { |
1317 | Fraction origZoomX = mrViewData.GetZoomX(); |
1318 | Fraction origZoomY = mrViewData.GetZoomY(); |
1319 | |
1320 | // Output size is in pixels while tile position and size are in logical units (twips). |
1321 | |
1322 | // Assumption: always paint the whole sheet i.e. "visible" range is always |
1323 | // from (0,0) to last data position. |
1324 | |
1325 | // Tile geometry is independent of the zoom level, but the output size is |
1326 | // dependent of the zoom level. Determine the correct zoom level before |
1327 | // we start. |
1328 | |
1329 | // FIXME the painting works using a mixture of drawing with coordinates in |
1330 | // pixels and in logic coordinates; it should be cleaned up to use logic |
1331 | // coords only, and avoid all the SetMapMode()'s. |
1332 | // Similarly to Writer, we should set the mapmode once on the rDevice, and |
1333 | // not care about any zoom settings. |
1334 | |
1335 | Fraction aFracX(long(nOutputWidth * TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0)), nTileWidth); |
1336 | Fraction aFracY(long(nOutputHeight * TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0)), nTileHeight); |
1337 | |
1338 | const bool bChangeZoom = (aFracX != origZoomX || aFracY != origZoomY); |
1339 | |
1340 | // page break zoom, and aLogicMode in ScViewData |
1341 | // FIXME: there are issues when SetZoom is called conditionally. |
1342 | mrViewData.SetZoom(aFracX, aFracY, true); |
1343 | if (bChangeZoom) |
1344 | { |
1345 | if (ScDrawView* pDrawView = mrViewData.GetScDrawView()) |
1346 | pDrawView->resetGridOffsetsForAllSdrPageViews(); |
1347 | } |
1348 | |
1349 | const double fTilePosXPixel = static_cast<double>(nTilePosX) * nOutputWidth / nTileWidth; |
1350 | const double fTilePosYPixel = static_cast<double>(nTilePosY) * nOutputHeight / nTileHeight; |
1351 | const double fTileBottomPixel = static_cast<double>(nTilePosY + nTileHeight) * nOutputHeight / nTileHeight; |
1352 | const double fTileRightPixel = static_cast<double>(nTilePosX + nTileWidth) * nOutputWidth / nTileWidth; |
1353 | |
1354 | SCTAB nTab = mrViewData.GetTabNo(); |
1355 | ScDocument& rDoc = mrViewData.GetDocument(); |
1356 | |
1357 | const double fPPTX = mrViewData.GetPPTX(); |
1358 | const double fPPTY = mrViewData.GetPPTY(); |
1359 | |
1360 | // find approximate col/row offsets of nearby. |
1361 | sal_Int32 nTopLeftTileRowOffset = 0; |
1362 | sal_Int32 nTopLeftTileColOffset = 0; |
1363 | sal_Int32 nTopLeftTileRowOrigin = 0; |
1364 | sal_Int32 nTopLeftTileColOrigin = 0; |
1365 | |
1366 | sal_Int32 nTopLeftTileRow = 0; |
1367 | sal_Int32 nTopLeftTileCol = 0; |
1368 | sal_Int32 nBottomRightTileRow = 0; |
1369 | sal_Int32 nBottomRightTileCol = 0; |
1370 | |
1371 | lcl_getBoundingRowColumnforTile<SCROW>(mrViewData, |
1372 | fTilePosYPixel, fTileBottomPixel, |
1373 | nTopLeftTileRowOffset, nTopLeftTileRowOrigin, |
1374 | nTopLeftTileRow, nBottomRightTileRow); |
1375 | |
1376 | lcl_getBoundingRowColumnforTile<SCCOL>(mrViewData, |
1377 | fTilePosXPixel, fTileRightPixel, |
1378 | nTopLeftTileColOffset, nTopLeftTileColOrigin, |
1379 | nTopLeftTileCol, nBottomRightTileCol); |
1380 | |
1381 | // Enlarge |
1382 | nBottomRightTileCol++; |
1383 | nBottomRightTileRow++; |
1384 | |
1385 | if (nBottomRightTileCol > rDoc.MaxCol()) |
1386 | nBottomRightTileCol = rDoc.MaxCol(); |
1387 | |
1388 | if (nBottomRightTileRow > MAXTILEDROW) |
1389 | nBottomRightTileRow = MAXTILEDROW; |
1390 | |
1391 | // size of the document including drawings, charts, etc. |
1392 | SCCOL nEndCol = 0; |
1393 | SCROW nEndRow = 0; |
1394 | rDoc.GetTiledRenderingArea(nTab, nEndCol, nEndRow); |
1395 | |
1396 | if (nEndCol < nBottomRightTileCol) |
1397 | nEndCol = nBottomRightTileCol; |
Value stored to 'nEndCol' is never read | |
1398 | |
1399 | if (nEndRow < nBottomRightTileRow) |
1400 | nEndRow = nBottomRightTileRow; |
1401 | |
1402 | nTopLeftTileCol = std::max<sal_Int32>(nTopLeftTileCol, 0); |
1403 | nTopLeftTileRow = std::max<sal_Int32>(nTopLeftTileRow, 0); |
1404 | nTopLeftTileColOrigin = nTopLeftTileColOrigin * TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0); |
1405 | nTopLeftTileRowOrigin = nTopLeftTileRowOrigin * TWIPS_PER_PIXEL((20.0 * 72.0) / 96.0); |
1406 | |
1407 | // Checkout -> 'rDoc.ExtendMerge' ... if we miss merged cells. |
1408 | |
1409 | // Origin must be the offset of the first col and row |
1410 | // containing our top-left pixel. |
1411 | const MapMode aOriginalMode = rDevice.GetMapMode(); |
1412 | MapMode aAbsMode = aOriginalMode; |
1413 | const Point aOrigin(-nTopLeftTileColOrigin, -nTopLeftTileRowOrigin); |
1414 | aAbsMode.SetOrigin(aOrigin); |
1415 | rDevice.SetMapMode(aAbsMode); |
1416 | |
1417 | ScTableInfo aTabInfo(nEndRow + 3); |
1418 | rDoc.FillInfo(aTabInfo, nTopLeftTileCol, nTopLeftTileRow, |
1419 | nBottomRightTileCol, nBottomRightTileRow, |
1420 | nTab, fPPTX, fPPTY, false, false); |
1421 | |
1422 | // FIXME: is this called some |
1423 | // Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich ); |
1424 | |
1425 | ScOutputData aOutputData(&rDevice, OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab, |
1426 | -nTopLeftTileColOffset, |
1427 | -nTopLeftTileRowOffset, |
1428 | nTopLeftTileCol, nTopLeftTileRow, |
1429 | nBottomRightTileCol, nBottomRightTileRow, |
1430 | fPPTX, fPPTY, nullptr, nullptr); |
1431 | |
1432 | // setup the SdrPage so that drawinglayer works correctly |
1433 | ScDrawLayer* pModel = rDoc.GetDrawLayer(); |
1434 | if (pModel) |
1435 | { |
1436 | bool bPrintTwipsMsgs = comphelper::LibreOfficeKit::isCompatFlagSet( |
1437 | comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs); |
1438 | mpLOKDrawView.reset(bPrintTwipsMsgs ? |
1439 | new ScLOKDrawView( |
1440 | &rDevice, |
1441 | mrViewData) : |
1442 | new FmFormView( |
1443 | *pModel, |
1444 | &rDevice)); |
1445 | mpLOKDrawView->ShowSdrPage(mpLOKDrawView->GetModel()->GetPage(nTab)); |
1446 | aOutputData.SetDrawView(mpLOKDrawView.get()); |
1447 | aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get()); |
1448 | } |
1449 | |
1450 | // draw the content |
1451 | DrawContent(rDevice, aTabInfo, aOutputData, true); |
1452 | rDevice.SetMapMode(aOriginalMode); |
1453 | |
1454 | // Flag drawn formula cells "unchanged". |
1455 | rDoc.ResetChanged(ScRange(nTopLeftTileCol, nTopLeftTileRow, nTab, nBottomRightTileCol, nBottomRightTileRow, nTab)); |
1456 | rDoc.PrepareFormulaCalc(); |
1457 | |
1458 | mrViewData.SetZoom(origZoomX, origZoomY, true); |
1459 | if (bChangeZoom) |
1460 | { |
1461 | if (ScDrawView* pDrawView = mrViewData.GetScDrawView()) |
1462 | pDrawView->resetGridOffsetsForAllSdrPageViews(); |
1463 | } |
1464 | } |
1465 | |
1466 | void ScGridWindow::LogicInvalidate(const tools::Rectangle* pRectangle) |
1467 | { |
1468 | OString sRectangle; |
1469 | if (!pRectangle) |
1470 | sRectangle = "EMPTY"; |
1471 | else |
1472 | { |
1473 | tools::Rectangle aRectangle(*pRectangle); |
1474 | // When dragging shapes the map mode is disabled. |
1475 | if (IsMapModeEnabled()) |
1476 | { |
1477 | if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM) |
1478 | aRectangle = OutputDevice::LogicToLogic(aRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip)); |
1479 | } |
1480 | else |
1481 | aRectangle = PixelToLogic(aRectangle, MapMode(MapUnit::MapTwip)); |
1482 | sRectangle = aRectangle.toString(); |
1483 | } |
1484 | |
1485 | ScTabViewShell* pViewShell = mrViewData.GetViewShell(); |
1486 | SfxLokHelper::notifyInvalidation(pViewShell, sRectangle); |
1487 | } |
1488 | |
1489 | void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY) |
1490 | { |
1491 | ScTabView* pTabView = mrViewData.GetView(); |
1492 | ScTabViewShell* pViewShell = mrViewData.GetViewShell(); |
1493 | ScInputHandler* pInputHandler = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule ::Calc)) )->GetInputHdl(pViewShell); |
1494 | |
1495 | if (pInputHandler && pInputHandler->IsInputMode()) |
1496 | { |
1497 | // we need to switch off the editeng |
1498 | ScTabView::UpdateInputLine(); |
1499 | pViewShell->UpdateInputHandler(); |
1500 | } |
1501 | |
1502 | if (nType == LOK_SETTEXTSELECTION_RESET) |
1503 | { |
1504 | pTabView->DoneBlockMode(); |
1505 | return; |
1506 | } |
1507 | |
1508 | // obtain the current selection |
1509 | ScRangeList aRangeList = mrViewData.GetMarkData().GetMarkedRanges(); |
1510 | |
1511 | SCCOL nCol1, nCol2; |
1512 | SCROW nRow1, nRow2; |
1513 | SCTAB nTab1, nTab2; |
1514 | |
1515 | bool bWasEmpty = false; |
1516 | if (aRangeList.empty()) |
1517 | { |
1518 | nCol1 = nCol2 = mrViewData.GetCurX(); |
1519 | nRow1 = nRow2 = mrViewData.GetCurY(); |
1520 | bWasEmpty = true; |
1521 | } |
1522 | else |
1523 | aRangeList.Combine().GetVars(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); |
1524 | |
1525 | // convert the coordinates to column/row |
1526 | SCCOL nNewPosX; |
1527 | SCROW nNewPosY; |
1528 | SCTAB nTab = mrViewData.GetTabNo(); |
1529 | mrViewData.GetPosFromPixel(nPixelX, nPixelY, eWhich, nNewPosX, nNewPosY); |
1530 | |
1531 | // change the selection |
1532 | switch (nType) |
1533 | { |
1534 | case LOK_SETTEXTSELECTION_START: |
1535 | if (nNewPosX != nCol1 || nNewPosY != nRow1 || bWasEmpty) |
1536 | { |
1537 | pTabView->SetCursor(nNewPosX, nNewPosY); |
1538 | pTabView->DoneBlockMode(); |
1539 | pTabView->InitBlockMode(nNewPosX, nNewPosY, nTab, true); |
1540 | pTabView->MarkCursor(nCol2, nRow2, nTab); |
1541 | } |
1542 | break; |
1543 | case LOK_SETTEXTSELECTION_END: |
1544 | if (nNewPosX != nCol2 || nNewPosY != nRow2 || bWasEmpty) |
1545 | { |
1546 | pTabView->SetCursor(nCol1, nRow1); |
1547 | pTabView->DoneBlockMode(); |
1548 | pTabView->InitBlockMode(nCol1, nRow1, nTab, true); |
1549 | pTabView->MarkCursor(nNewPosX, nNewPosY, nTab); |
1550 | } |
1551 | break; |
1552 | default: |
1553 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", "/home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx" , 1553, __extension__ __PRETTY_FUNCTION__)); |
1554 | break; |
1555 | } |
1556 | } |
1557 | |
1558 | void ScGridWindow::CheckNeedsRepaint() |
1559 | { |
1560 | // called at the end of painting, and from timer after background text width calculation |
1561 | |
1562 | if (!bNeedsRepaint) |
1563 | return; |
1564 | |
1565 | bNeedsRepaint = false; |
1566 | if (aRepaintPixel.IsEmpty()) |
1567 | Invalidate(); |
1568 | else |
1569 | Invalidate(PixelToLogic(aRepaintPixel)); |
1570 | aRepaintPixel = tools::Rectangle(); |
1571 | |
1572 | // selection function in status bar might also be invalid |
1573 | SfxBindings& rBindings = mrViewData.GetBindings(); |
1574 | rBindings.Invalidate( SID_STATUS_SUM((26000 + 100) + 30) ); |
1575 | rBindings.Invalidate( SID_ATTR_SIZETypedWhichId<SvxSizeItem>( 10000 + 224 ) ); |
1576 | rBindings.Invalidate( SID_TABLE_CELL( 10000 + 225 ) ); |
1577 | } |
1578 | |
1579 | void ScGridWindow::DrawPagePreview( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext) |
1580 | { |
1581 | ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData(); |
1582 | if (!pPageData) |
1583 | return; |
1584 | |
1585 | ScDocument& rDoc = mrViewData.GetDocument(); |
1586 | SCTAB nTab = mrViewData.GetTabNo(); |
1587 | Size aWinSize = GetOutputSizePixel(); |
1588 | const svtools::ColorConfig& rColorCfg = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule ::Calc)) )->GetColorConfig(); |
1589 | Color aManual( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor ); |
1590 | Color aAutomatic( rColorCfg.GetColorValue(svtools::CALCPAGEBREAK).nColor ); |
1591 | |
1592 | OUString aPageStr = ScResId( STR_PGNUMreinterpret_cast<char const *>("STR_PGNUM" "\004" u8"Page %1" ) ); |
1593 | if ( nPageScript == SvtScriptType::NONE ) |
1594 | { |
1595 | // get script type of translated "Page" string only once |
1596 | nPageScript = rDoc.GetStringScriptType( aPageStr ); |
1597 | if (nPageScript == SvtScriptType::NONE) |
1598 | nPageScript = ScGlobal::GetDefaultScriptType(); |
1599 | } |
1600 | |
1601 | vcl::Font aFont; |
1602 | std::unique_ptr<ScEditEngineDefaulter> pEditEng; |
1603 | const ScPatternAttr& rDefPattern = rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN); |
1604 | if ( nPageScript == SvtScriptType::LATIN ) |
1605 | { |
1606 | // use single font and call DrawText directly |
1607 | rDefPattern.GetFont( aFont, SC_AUTOCOL_BLACK ); |
1608 | aFont.SetColor( COL_LIGHTGRAY ); |
1609 | // font size is set as needed |
1610 | } |
1611 | else |
1612 | { |
1613 | // use EditEngine to draw mixed-script string |
1614 | pEditEng.reset(new ScEditEngineDefaulter( EditEngine::CreatePool(), true )); |
1615 | pEditEng->SetRefMapMode(rRenderContext.GetMapMode()); |
1616 | auto pEditDefaults = std::make_unique<SfxItemSet>( pEditEng->GetEmptyItemSet() ); |
1617 | rDefPattern.FillEditItemSet( pEditDefaults.get() ); |
1618 | pEditDefaults->Put( SvxColorItem( COL_LIGHTGRAY, EE_CHAR_COLOR ) ); |
1619 | pEditEng->SetDefaults( std::move(pEditDefaults) ); |
1620 | } |
1621 | |
1622 | sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() ); |
1623 | for (sal_uInt16 nPos=0; nPos<nCount; nPos++) |
1624 | { |
1625 | ScPrintRangeData& rData = pPageData->GetData(nPos); |
1626 | ScRange aRange = rData.GetPrintRange(); |
1627 | if ( aRange.aStart.Col() <= nX2+1 && aRange.aEnd.Col()+1 >= nX1 && |
1628 | aRange.aStart.Row() <= nY2+1 && aRange.aEnd.Row()+1 >= nY1 ) |
1629 | { |
1630 | // 3 pixel frame around the print area |
1631 | // (middle pixel on the grid lines) |
1632 | |
1633 | rRenderContext.SetLineColor(); |
1634 | if (rData.IsAutomatic()) |
1635 | rRenderContext.SetFillColor( aAutomatic ); |
1636 | else |
1637 | rRenderContext.SetFillColor( aManual ); |
1638 | |
1639 | Point aStart = mrViewData.GetScrPos( |
1640 | aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true ); |
1641 | Point aEnd = mrViewData.GetScrPos( |
1642 | aRange.aEnd.Col() + 1, aRange.aEnd.Row() + 1, eWhich, true ); |
1643 | aStart.AdjustX( -2 ); |
1644 | aStart.AdjustY( -2 ); |
1645 | |
1646 | // Prevent overflows: |
1647 | if ( aStart.X() < -10 ) aStart.setX( -10 ); |
1648 | if ( aStart.Y() < -10 ) aStart.setY( -10 ); |
1649 | if ( aEnd.X() > aWinSize.Width() + 10 ) |
1650 | aEnd.setX( aWinSize.Width() + 10 ); |
1651 | if ( aEnd.Y() > aWinSize.Height() + 10 ) |
1652 | aEnd.setY( aWinSize.Height() + 10 ); |
1653 | |
1654 | rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aEnd.X(),aStart.Y()+2) ) ); |
1655 | rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aStart.X()+2,aEnd.Y()) ) ); |
1656 | rRenderContext.DrawRect( tools::Rectangle( Point(aStart.X(),aEnd.Y()-2), aEnd ) ); |
1657 | rRenderContext.DrawRect( tools::Rectangle( Point(aEnd.X()-2,aStart.Y()), aEnd ) ); |
1658 | |
1659 | // Page breaks |
1660 | //! Display differently (dashed ????) |
1661 | |
1662 | size_t nColBreaks = rData.GetPagesX(); |
1663 | const SCCOL* pColEnd = rData.GetPageEndX(); |
1664 | size_t nColPos; |
1665 | for (nColPos=0; nColPos+1<nColBreaks; nColPos++) |
1666 | { |
1667 | SCCOL nBreak = pColEnd[nColPos]+1; |
1668 | if ( nBreak >= nX1 && nBreak <= nX2+1 ) |
1669 | { |
1670 | //! Search for hidden |
1671 | if (rDoc.HasColBreak(nBreak, nTab) & ScBreakType::Manual) |
1672 | rRenderContext.SetFillColor( aManual ); |
1673 | else |
1674 | rRenderContext.SetFillColor( aAutomatic ); |
1675 | Point aBreak = mrViewData.GetScrPos( |
1676 | nBreak, aRange.aStart.Row(), eWhich, true ); |
1677 | rRenderContext.DrawRect( tools::Rectangle( aBreak.X()-1, aStart.Y(), aBreak.X(), aEnd.Y() ) ); |
1678 | } |
1679 | } |
1680 | |
1681 | size_t nRowBreaks = rData.GetPagesY(); |
1682 | const SCROW* pRowEnd = rData.GetPageEndY(); |
1683 | size_t nRowPos; |
1684 | for (nRowPos=0; nRowPos+1<nRowBreaks; nRowPos++) |
1685 | { |
1686 | SCROW nBreak = pRowEnd[nRowPos]+1; |
1687 | if ( nBreak >= nY1 && nBreak <= nY2+1 ) |
1688 | { |
1689 | //! Search for hidden |
1690 | if (rDoc.HasRowBreak(nBreak, nTab) & ScBreakType::Manual) |
1691 | rRenderContext.SetFillColor( aManual ); |
1692 | else |
1693 | rRenderContext.SetFillColor( aAutomatic ); |
1694 | Point aBreak = mrViewData.GetScrPos( |
1695 | aRange.aStart.Col(), nBreak, eWhich, true ); |
1696 | rRenderContext.DrawRect( tools::Rectangle( aStart.X(), aBreak.Y()-1, aEnd.X(), aBreak.Y() ) ); |
1697 | } |
1698 | } |
1699 | |
1700 | // Page numbers |
1701 | |
1702 | SCROW nPrStartY = aRange.aStart.Row(); |
1703 | for (nRowPos=0; nRowPos<nRowBreaks; nRowPos++) |
1704 | { |
1705 | SCROW nPrEndY = pRowEnd[nRowPos]; |
1706 | if ( nPrEndY >= nY1 && nPrStartY <= nY2 ) |
1707 | { |
1708 | SCCOL nPrStartX = aRange.aStart.Col(); |
1709 | for (nColPos=0; nColPos<nColBreaks; nColPos++) |
1710 | { |
1711 | SCCOL nPrEndX = pColEnd[nColPos]; |
1712 | if ( nPrEndX >= nX1 && nPrStartX <= nX2 ) |
1713 | { |
1714 | Point aPageStart = mrViewData.GetScrPos( |
1715 | nPrStartX, nPrStartY, eWhich, true ); |
1716 | Point aPageEnd = mrViewData.GetScrPos( |
1717 | nPrEndX+1,nPrEndY+1, eWhich, true ); |
1718 | |
1719 | long nPageNo = rData.GetFirstPage(); |
1720 | if ( rData.IsTopDown() ) |
1721 | nPageNo += static_cast<long>(nColPos)*nRowBreaks+nRowPos; |
1722 | else |
1723 | nPageNo += static_cast<long>(nRowPos)*nColBreaks+nColPos; |
1724 | |
1725 | OUString aThisPageStr = aPageStr.replaceFirst("%1", OUString::number(nPageNo)); |
1726 | |
1727 | if ( pEditEng ) |
1728 | { |
1729 | // find right font size with EditEngine |
1730 | long nHeight = 100; |
1731 | pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) ); |
1732 | pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) ); |
1733 | pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) ); |
1734 | pEditEng->SetTextCurrentDefaults( aThisPageStr ); |
1735 | Size aSize100( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() ); |
1736 | |
1737 | // 40% of width or 60% of height |
1738 | long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width(); |
1739 | long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height(); |
1740 | nHeight = std::min(nSizeX,nSizeY); |
1741 | pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) ); |
1742 | pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) ); |
1743 | pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) ); |
1744 | |
1745 | // centered output with EditEngine |
1746 | Size aTextSize( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() ); |
1747 | Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2, |
1748 | (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 ); |
1749 | pEditEng->Draw( &rRenderContext, aPos ); |
1750 | } |
1751 | else |
1752 | { |
1753 | // find right font size for DrawText |
1754 | aFont.SetFontSize( Size( 0,100 ) ); |
1755 | rRenderContext.SetFont( aFont ); |
1756 | Size aSize100(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() ); |
1757 | |
1758 | // 40% of width or 60% of height |
1759 | long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width(); |
1760 | long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height(); |
1761 | aFont.SetFontSize( Size( 0,std::min(nSizeX,nSizeY) ) ); |
1762 | rRenderContext.SetFont( aFont ); |
1763 | |
1764 | // centered output with DrawText |
1765 | Size aTextSize(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() ); |
1766 | Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2, |
1767 | (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 ); |
1768 | rRenderContext.DrawText( aPos, aThisPageStr ); |
1769 | } |
1770 | } |
1771 | nPrStartX = nPrEndX + 1; |
1772 | } |
1773 | } |
1774 | nPrStartY = nPrEndY + 1; |
1775 | } |
1776 | } |
1777 | } |
1778 | } |
1779 | |
1780 | void ScGridWindow::DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo, OutputDevice* pContentDev) |
1781 | { |
1782 | aComboButton.SetOutputDevice( pContentDev ); |
1783 | |
1784 | ScDocument& rDoc = mrViewData.GetDocument(); |
1785 | ScDPFieldButton aCellBtn(pContentDev, &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc); |
1786 | |
1787 | SCCOL nCol; |
1788 | SCROW nRow; |
1789 | SCSIZE nArrY; |
1790 | SCSIZE nQuery; |
1791 | SCTAB nTab = mrViewData.GetTabNo(); |
1792 | ScDBData* pDBData = nullptr; |
1793 | std::unique_ptr<ScQueryParam> pQueryParam; |
1794 | |
1795 | RowInfo* pRowInfo = rTabInfo.mpRowInfo.get(); |
1796 | sal_uInt16 nArrCount = rTabInfo.mnArrCount; |
1797 | |
1798 | bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); |
1799 | |
1800 | Point aOldPos = aComboButton.GetPosPixel(); // store state for MouseDown/Up |
1801 | Size aOldSize = aComboButton.GetSizePixel(); |
1802 | |
1803 | for (nArrY=1; nArrY+1<nArrCount; nArrY++) |
1804 | { |
1805 | if ( pRowInfo[nArrY].bAutoFilter && pRowInfo[nArrY].bChanged ) |
1806 | { |
1807 | RowInfo* pThisRowInfo = &pRowInfo[nArrY]; |
1808 | |
1809 | nRow = pThisRowInfo->nRowNo; |
1810 | |
1811 | for (nCol=nX1; nCol<=nX2; nCol++) |
1812 | { |
1813 | CellInfo* pInfo = &pThisRowInfo->pCellInfo[nCol+1]; |
1814 | //if several columns merged on a row, there should be only one auto button at the end of the columns. |
1815 | //if several rows merged on a column, the button may be in the middle, so "!pInfo->bVOverlapped" should not be used |
1816 | if ( pInfo->bAutoFilter && !pInfo->bHOverlapped ) |
1817 | { |
1818 | if (!pQueryParam) |
1819 | pQueryParam.reset(new ScQueryParam); |
1820 | |
1821 | bool bNewData = true; |
1822 | if (pDBData) |
1823 | { |
1824 | SCCOL nStartCol; |
1825 | SCROW nStartRow; |
1826 | SCCOL nEndCol; |
1827 | SCROW nEndRow; |
1828 | SCTAB nAreaTab; |
1829 | pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow ); |
1830 | if ( nCol >= nStartCol && nCol <= nEndCol && |
1831 | nRow >= nStartRow && nRow <= nEndRow ) |
1832 | bNewData = false; |
1833 | } |
1834 | if (bNewData) |
1835 | { |
1836 | pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA ); |
1837 | if (pDBData) |
1838 | pDBData->GetQueryParam( *pQueryParam ); |
1839 | else |
1840 | { |
1841 | // can also be part of DataPilot table |
1842 | } |
1843 | } |
1844 | |
1845 | // pQueryParam can only include MAXQUERY entries |
1846 | |
1847 | bool bSimpleQuery = true; |
1848 | bool bColumnFound = false; |
1849 | if (!pQueryParam->bInplace) |
1850 | bSimpleQuery = false; |
1851 | SCSIZE nCount = pQueryParam->GetEntryCount(); |
1852 | for (nQuery = 0; nQuery < nCount && bSimpleQuery; ++nQuery) |
1853 | if (pQueryParam->GetEntry(nQuery).bDoQuery) |
1854 | { |
1855 | // Do no restrict to EQUAL here |
1856 | // (Column head should become blue also when ">1") |
1857 | |
1858 | if (pQueryParam->GetEntry(nQuery).nField == nCol) |
1859 | bColumnFound = true; |
1860 | if (nQuery > 0) |
1861 | if (pQueryParam->GetEntry(nQuery).eConnect != SC_AND) |
1862 | bSimpleQuery = false; |
1863 | } |
1864 | |
1865 | bool bArrowState = bSimpleQuery && bColumnFound; |
1866 | long nSizeX; |
1867 | long nSizeY; |
1868 | SCCOL nStartCol= nCol; |
1869 | SCROW nStartRow = nRow; |
1870 | //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. |
1871 | //should first get the start pos of the merge area, then get the nSizeX through the start pos. |
1872 | rDoc.ExtendOverlapped(nStartCol, nStartRow,nCol, nRow, nTab);//get nStartCol,nStartRow |
1873 | mrViewData.GetMergeSizePixel( nStartCol, nStartRow, nSizeX, nSizeY );//get nSizeX |
1874 | nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY()); |
1875 | Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich ); |
1876 | |
1877 | aCellBtn.setBoundingBox(aScrPos, Size(nSizeX-1, nSizeY-1), bLayoutRTL); |
1878 | aCellBtn.setPopupLeft(bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL |
1879 | aCellBtn.setDrawBaseButton(false); |
1880 | aCellBtn.setDrawPopupButton(true); |
1881 | aCellBtn.setHasHiddenMember(bArrowState); |
1882 | aCellBtn.draw(); |
1883 | } |
1884 | } |
1885 | } |
1886 | |
1887 | if ( pRowInfo[nArrY].bPivotButton && pRowInfo[nArrY].bChanged ) |
1888 | { |
1889 | RowInfo* pThisRowInfo = &pRowInfo[nArrY]; |
1890 | nRow = pThisRowInfo->nRowNo; |
1891 | for (nCol=nX1; nCol<=nX2; nCol++) |
1892 | { |
1893 | CellInfo* pInfo = &pThisRowInfo->pCellInfo[nCol+1]; |
1894 | if (pInfo->bHOverlapped || pInfo->bVOverlapped) |
1895 | continue; |
1896 | |
1897 | Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich ); |
1898 | long nSizeX; |
1899 | long nSizeY; |
1900 | mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY ); |
1901 | long nPosX = aScrPos.X(); |
1902 | long nPosY = aScrPos.Y(); |
1903 | // bLayoutRTL is handled in setBoundingBox |
1904 | |
1905 | OUString aStr = rDoc.GetString(nCol, nRow, nTab); |
1906 | aCellBtn.setText(aStr); |
1907 | aCellBtn.setBoundingBox(Point(nPosX, nPosY), Size(nSizeX-1, nSizeY-1), bLayoutRTL); |
1908 | aCellBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now |
1909 | aCellBtn.setDrawBaseButton(pInfo->bPivotButton); |
1910 | aCellBtn.setDrawPopupButton(pInfo->bPivotPopupButton); |
1911 | aCellBtn.setHasHiddenMember(pInfo->bFilterActive); |
1912 | aCellBtn.draw(); |
1913 | } |
1914 | } |
1915 | |
1916 | if ( !comphelper::LibreOfficeKit::isActive() && bListValButton && pRowInfo[nArrY].nRowNo == aListValPos.Row() && pRowInfo[nArrY].bChanged ) |
1917 | { |
1918 | tools::Rectangle aRect = GetListValButtonRect( aListValPos ); |
1919 | aComboButton.SetPosPixel( aRect.TopLeft() ); |
1920 | aComboButton.SetSizePixel( aRect.GetSize() ); |
1921 | pContentDev->SetClipRegion(vcl::Region(aRect)); |
1922 | aComboButton.Draw(); |
1923 | pContentDev->SetClipRegion(); // always called from Draw() without clip region |
1924 | aComboButton.SetPosPixel( aOldPos ); // restore old state |
1925 | aComboButton.SetSizePixel( aOldSize ); // for MouseUp/Down (AutoFilter) |
1926 | } |
1927 | } |
1928 | |
1929 | pQueryParam.reset(); |
1930 | aComboButton.SetOutputDevice( this ); |
1931 | } |
1932 | |
1933 | tools::Rectangle ScGridWindow::GetListValButtonRect( const ScAddress& rButtonPos ) |
1934 | { |
1935 | ScDocument& rDoc = mrViewData.GetDocument(); |
1936 | SCTAB nTab = mrViewData.GetTabNo(); |
1937 | bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); |
1938 | long nLayoutSign = bLayoutRTL ? -1 : 1; |
1939 | |
1940 | ScDDComboBoxButton aButton( this ); // for optimal size |
1941 | Size aBtnSize = aButton.GetSizePixel(); |
1942 | |
1943 | SCCOL nCol = rButtonPos.Col(); |
1944 | SCROW nRow = rButtonPos.Row(); |
1945 | |
1946 | long nCellSizeX; // width of this cell, including merged |
1947 | long nDummy; |
1948 | mrViewData.GetMergeSizePixel( nCol, nRow, nCellSizeX, nDummy ); |
1949 | |
1950 | // for height, only the cell's row is used, excluding merged cells |
1951 | long nCellSizeY = ScViewData::ToPixel( rDoc.GetRowHeight( nRow, nTab ), mrViewData.GetPPTY() ); |
1952 | long nAvailable = nCellSizeX; |
1953 | |
1954 | // left edge of next cell if there is a non-hidden next column |
1955 | SCCOL nNextCol = nCol + 1; |
1956 | const ScMergeAttr* pMerge = rDoc.GetAttr( nCol,nRow,nTab, ATTR_MERGE ); |
1957 | if ( pMerge->GetColMerge() > 1 ) |
1958 | nNextCol = nCol + pMerge->GetColMerge(); // next cell after the merged area |
1959 | while ( nNextCol <= rDoc.MaxCol() && rDoc.ColHidden(nNextCol, nTab) ) |
1960 | ++nNextCol; |
1961 | bool bNextCell = ( nNextCol <= rDoc.MaxCol() ); |
1962 | if ( bNextCell ) |
1963 | nAvailable = ScViewData::ToPixel( rDoc.GetColWidth( nNextCol, nTab ), mrViewData.GetPPTX() ); |
1964 | |
1965 | if ( nAvailable < aBtnSize.Width() ) |
1966 | aBtnSize.setWidth( nAvailable ); |
1967 | if ( nCellSizeY < aBtnSize.Height() ) |
1968 | aBtnSize.setHeight( nCellSizeY ); |
1969 | |
1970 | Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich, true ); |
1971 | aPos.AdjustX(nCellSizeX * nLayoutSign ); // start of next cell |
1972 | if (!bNextCell) |
1973 | aPos.AdjustX( -(aBtnSize.Width() * nLayoutSign) ); // right edge of cell if next cell not available |
1974 | aPos.AdjustY(nCellSizeY - aBtnSize.Height() ); |
1975 | // X remains at the left edge |
1976 | |
1977 | if ( bLayoutRTL ) |
1978 | aPos.AdjustX( -(aBtnSize.Width()-1) ); // align right edge of button with cell border |
1979 | |
1980 | return tools::Rectangle( aPos, aBtnSize ); |
1981 | } |
1982 | |
1983 | bool ScGridWindow::IsAutoFilterActive( SCCOL nCol, SCROW nRow, SCTAB nTab ) |
1984 | { |
1985 | ScDocument& rDoc = mrViewData.GetDocument(); |
1986 | ScDBData* pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA ); |
1987 | ScQueryParam aQueryParam; |
1988 | |
1989 | if ( pDBData ) |
1990 | pDBData->GetQueryParam( aQueryParam ); |
1991 | else |
1992 | { |
1993 | OSL_FAIL("Auto filter button without DBData")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/view/gridwin4.cxx" ":" "1993" ": "), "%s", "Auto filter button without DBData") ; } } while (false); |
1994 | } |
1995 | |
1996 | bool bSimpleQuery = true; |
1997 | bool bColumnFound = false; |
1998 | SCSIZE nQuery; |
1999 | |
2000 | if ( !aQueryParam.bInplace ) |
2001 | bSimpleQuery = false; |
2002 | |
2003 | // aQueryParam can only include MAXQUERY entries |
2004 | |
2005 | SCSIZE nCount = aQueryParam.GetEntryCount(); |
2006 | for (nQuery = 0; nQuery < nCount && bSimpleQuery; ++nQuery) |
2007 | if ( aQueryParam.GetEntry(nQuery).bDoQuery ) |
2008 | { |
2009 | if (aQueryParam.GetEntry(nQuery).nField == nCol) |
2010 | bColumnFound = true; |
2011 | |
2012 | if (nQuery > 0) |
2013 | if (aQueryParam.GetEntry(nQuery).eConnect != SC_AND) |
2014 | bSimpleQuery = false; |
2015 | } |
2016 | |
2017 | return ( bSimpleQuery && bColumnFound ); |
2018 | } |
2019 | |
2020 | void ScGridWindow::GetSelectionRects( ::std::vector< tools::Rectangle >& rPixelRects ) const |
2021 | { |
2022 | GetPixelRectsFor( mrViewData.GetMarkData(), rPixelRects ); |
2023 | } |
2024 | |
2025 | void ScGridWindow::GetSelectionRectsPrintTwips(::std::vector< tools::Rectangle >& rRects) const |
2026 | { |
2027 | GetRectsAnyFor(mrViewData.GetMarkData(), rRects, true); |
2028 | } |
2029 | |
2030 | /// convert rMarkData into pixel rectangles for this view |
2031 | void ScGridWindow::GetPixelRectsFor( const ScMarkData &rMarkData, |
2032 | ::std::vector< tools::Rectangle >& rPixelRects ) const |
2033 | { |
2034 | GetRectsAnyFor(rMarkData, rPixelRects, false); |
2035 | } |
2036 | |
2037 | void ScGridWindow::GetRectsAnyFor(const ScMarkData &rMarkData, |
2038 | ::std::vector< tools::Rectangle >& rRects, |
2039 | bool bInPrintTwips) const |
2040 | { |
2041 | ScMarkData aMultiMark( rMarkData ); |
2042 | aMultiMark.SetMarking( false ); |
2043 | aMultiMark.MarkToMulti(); |
2044 | ScDocument& rDoc = mrViewData.GetDocument(); |
2045 | SCTAB nTab = mrViewData.GetTabNo(); |
2046 | |
2047 | bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); |
2048 | long nLayoutSign = bLayoutRTL ? -1 : 1; |
2049 | if ( !aMultiMark.IsMultiMarked() ) |
2050 | return; |
2051 | ScRange aMultiRange; |
2052 | aMultiMark.GetMultiMarkArea( aMultiRange ); |
2053 | SCCOL nX1 = aMultiRange.aStart.Col(); |
2054 | SCROW nY1 = aMultiRange.aStart.Row(); |
2055 | SCCOL nX2 = aMultiRange.aEnd.Col(); |
2056 | SCROW nY2 = aMultiRange.aEnd.Row(); |
2057 | |
2058 | PutInOrder( nX1, nX2 ); |
2059 | PutInOrder( nY1, nY2 ); |
2060 | |
2061 | SCCOL nTestX2 = nX2; |
2062 | SCROW nTestY2 = nY2; |
2063 | |
2064 | rDoc.ExtendMerge( nX1,nY1, nTestX2,nTestY2, nTab ); |
2065 | |
2066 | SCCOL nPosX = mrViewData.GetPosX( eHWhich ); |
2067 | SCROW nPosY = mrViewData.GetPosY( eVWhich ); |
2068 | // is the selection visible at all? |
2069 | if (nTestX2 < nPosX || nTestY2 < nPosY) |
2070 | return; |
2071 | SCCOL nRealX1 = nX1; |
2072 | if (nX1 < nPosX) |
2073 | nX1 = nPosX; |
2074 | if (nY1 < nPosY) |
2075 | nY1 = nPosY; |
2076 | |
2077 | if (!comphelper::LibreOfficeKit::isActive()) |
2078 | { |
2079 | // limit the selection to only what is visible on the screen |
2080 | SCCOL nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich); |
2081 | if (nXRight > rDoc.MaxCol()) |
2082 | nXRight = rDoc.MaxCol(); |
2083 | |
2084 | SCROW nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich); |
2085 | if (nYBottom > rDoc.MaxRow()) |
2086 | nYBottom = rDoc.MaxRow(); |
2087 | |
2088 | // is the selection visible at all? |
2089 | if (nX1 > nXRight || nY1 > nYBottom) |
2090 | return; |
2091 | |
2092 | if (nX2 > nXRight) |
2093 | nX2 = nXRight; |
2094 | if (nY2 > nYBottom) |
2095 | nY2 = nYBottom; |
2096 | } |
2097 | else |
2098 | { |
2099 | SCCOL nMaxTiledCol; |
2100 | SCROW nMaxTiledRow; |
2101 | rDoc.GetTiledRenderingArea(nTab, nMaxTiledCol, nMaxTiledRow); |
2102 | |
2103 | if (nX2 > nMaxTiledCol) |
2104 | nX2 = nMaxTiledCol; |
2105 | if (nY2 > nMaxTiledRow) |
2106 | nY2 = nMaxTiledRow; |
2107 | } |
2108 | |
2109 | double nPPTX = mrViewData.GetPPTX(); |
2110 | double nPPTY = mrViewData.GetPPTY(); |
2111 | |
2112 | ScInvertMerger aInvert( &rRects ); |
2113 | |
2114 | Point aScrPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) : |
2115 | mrViewData.GetScrPos(nX1, nY1, eWhich); |
2116 | long nScrY = aScrPos.Y(); |
2117 | bool bWasHidden = false; |
2118 | for (SCROW nY=nY1; nY<=nY2; nY++) |
2119 | { |
2120 | bool bFirstRow = ( nY == nPosY ); // first visible row? |
2121 | bool bDoHidden = false; // repeat hidden ? |
2122 | sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY,nTab ); |
2123 | bool bDoRow = ( nHeightTwips != 0 ); |
2124 | if (bDoRow) |
2125 | { |
2126 | if (bWasHidden) // test hidden merge |
2127 | { |
2128 | bDoHidden = true; |
2129 | bDoRow = true; |
2130 | } |
2131 | |
2132 | bWasHidden = false; |
2133 | } |
2134 | else |
2135 | { |
2136 | bWasHidden = true; |
2137 | if (nY==nY2) |
2138 | bDoRow = true; // last cell of the block |
2139 | } |
2140 | |
2141 | if ( bDoRow ) |
2142 | { |
2143 | SCCOL nLoopEndX = nX2; |
2144 | if (nX2 < nX1) // the rest of the merge |
2145 | { |
2146 | SCCOL nStartX = nX1; |
2147 | while ( rDoc.GetAttr(nStartX,nY,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped() ) |
2148 | --nStartX; |
2149 | if (nStartX <= nX2) |
2150 | nLoopEndX = nX1; |
2151 | } |
2152 | |
2153 | const long nHeight = bInPrintTwips ? |
2154 | nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY); |
2155 | long nEndY = nScrY + nHeight - 1; |
2156 | long nScrX = aScrPos.X(); |
2157 | for (SCCOL nX=nX1; nX<=nLoopEndX; nX++) |
2158 | { |
2159 | long nWidth = rDoc.GetColWidth(nX, nTab); |
2160 | if (!bInPrintTwips) |
2161 | nWidth = ScViewData::ToPixel(nWidth, nPPTX); |
2162 | |
2163 | if ( nWidth > 0 ) |
2164 | { |
2165 | long nEndX = nScrX + ( nWidth - 1 ) * nLayoutSign; |
2166 | |
2167 | SCROW nThisY = nY; |
2168 | const ScPatternAttr* pPattern = rDoc.GetPattern( nX, nY, nTab ); |
2169 | const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG); |
2170 | if ( pMergeFlag->IsVerOverlapped() && ( bDoHidden || bFirstRow ) ) |
2171 | { |
2172 | while ( pMergeFlag->IsVerOverlapped() && nThisY > 0 && |
2173 | (rDoc.RowHidden(nThisY-1, nTab) || bFirstRow) ) |
2174 | { |
2175 | --nThisY; |
2176 | pPattern = rDoc.GetPattern( nX, nThisY, nTab ); |
2177 | pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG); |
2178 | } |
2179 | } |
2180 | |
2181 | // only the rest of the merged is seen ? |
2182 | SCCOL nThisX = nX; |
2183 | if ( pMergeFlag->IsHorOverlapped() && nX == nPosX && nX > nRealX1 ) |
2184 | { |
2185 | while ( pMergeFlag->IsHorOverlapped() ) |
2186 | { |
2187 | --nThisX; |
2188 | pPattern = rDoc.GetPattern( nThisX, nThisY, nTab ); |
2189 | pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG); |
2190 | } |
2191 | } |
2192 | |
2193 | if ( aMultiMark.IsCellMarked( nThisX, nThisY, true ) ) |
2194 | { |
2195 | if ( !pMergeFlag->IsOverlapped() ) |
2196 | { |
2197 | const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE); |
2198 | if (pMerge->GetColMerge() > 0 || pMerge->GetRowMerge() > 0) |
2199 | { |
2200 | const SCCOL nEndColMerge = nThisX + pMerge->GetColMerge(); |
2201 | const SCROW nEndRowMerge = nThisY + pMerge->GetRowMerge(); |
2202 | Point aEndPos = bInPrintTwips ? |
2203 | mrViewData.GetPrintTwipsPos(nEndColMerge, nEndRowMerge) : |
2204 | mrViewData.GetScrPos(nEndColMerge, nEndRowMerge, eWhich); |
2205 | if ( aEndPos.X() * nLayoutSign > nScrX * nLayoutSign && aEndPos.Y() > nScrY ) |
2206 | { |
2207 | aInvert.AddRect( tools::Rectangle( nScrX,nScrY, |
2208 | aEndPos.X()-nLayoutSign,aEndPos.Y()-1 ) ); |
2209 | } |
2210 | } |
2211 | else if ( nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY ) |
2212 | { |
2213 | aInvert.AddRect( tools::Rectangle( nScrX,nScrY,nEndX,nEndY ) ); |
2214 | } |
2215 | } |
2216 | } |
2217 | |
2218 | nScrX = nEndX + nLayoutSign; |
2219 | } |
2220 | } |
2221 | nScrY = nEndY + 1; |
2222 | } |
2223 | } |
2224 | } |
2225 | |
2226 | void ScGridWindow::DataChanged( const DataChangedEvent& rDCEvt ) |
2227 | { |
2228 | Window::DataChanged(rDCEvt); |
2229 | |
2230 | if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) || |
2231 | (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || |
2232 | (rDCEvt.GetType() == DataChangedEventType::FONTS) || |
2233 | (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || |
2234 | ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && |
2235 | (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) ) |
2236 | return; |
2237 | |
2238 | if ( rDCEvt.GetType() == DataChangedEventType::FONTS && eWhich == mrViewData.GetActivePart() ) |
2239 | mrViewData.GetDocShell()->UpdateFontList(); |
2240 | |
2241 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && |
2242 | (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) |
2243 | { |
2244 | if ( eWhich == mrViewData.GetActivePart() ) // only once for the view |
2245 | { |
2246 | ScTabView* pView = mrViewData.GetView(); |
2247 | |
2248 | pView->RecalcPPT(); |
2249 | |
2250 | // RepeatResize in case scroll bar sizes have changed |
2251 | pView->RepeatResize(); |
2252 | pView->UpdateAllOverlays(); |
2253 | |
2254 | // invalidate cell attribs in input handler, in case the |
2255 | // EditEngine BackgroundColor has to be changed |
2256 | if ( mrViewData.IsActive() ) |
2257 | { |
2258 | ScInputHandler* pHdl = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule ::Calc)) )->GetInputHdl(); |
2259 | if (pHdl) |
2260 | pHdl->ForgetLastPattern(); |
2261 | } |
2262 | } |
2263 | } |
2264 | |
2265 | Invalidate(); |
2266 | } |
2267 | |
2268 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |