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 <comphelper/lok.hxx>
21 #include <osl/diagnose.h>
22 #include <svx/sdrpaintwindow.hxx>
23 #include <sdr/overlay/overlaymanagerbuffered.hxx>
24 #include <svx/svdpntv.hxx>
25 #include <vcl/gdimtf.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/window.hxx>
29 #include <svtools/optionsdrawinglayer.hxx>
30 #include <officecfg/Office/Common.hxx>
31 #include <set>
32 #include <vector>
33
34 namespace {
35
36 //rhbz#1007697 do this in two loops, one to collect the candidates
37 //and another to update them because updating a candidate can
38 //trigger the candidate to be deleted, so asking for its
39 //sibling after that is going to fail hard
40 class CandidateMgr
41 {
42 std::vector<VclPtr<vcl::Window> > m_aCandidates;
43 std::set<VclPtr<vcl::Window> > m_aDeletedCandidates;
44 DECL_LINK(WindowEventListener, VclWindowEvent&, void);
45 public:
46 void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect);
47 ~CandidateMgr();
48 };
49
50 }
51
IMPL_LINK(CandidateMgr,WindowEventListener,VclWindowEvent &,rEvent,void)52 IMPL_LINK(CandidateMgr, WindowEventListener, VclWindowEvent&, rEvent, void)
53 {
54 vcl::Window* pWindow = rEvent.GetWindow();
55 if (rEvent.GetId() == VclEventId::ObjectDying)
56 {
57 m_aDeletedCandidates.insert(pWindow);
58 }
59 }
60
~CandidateMgr()61 CandidateMgr::~CandidateMgr()
62 {
63 for (VclPtr<vcl::Window>& pCandidate : m_aCandidates)
64 {
65 if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
66 continue;
67 pCandidate->RemoveEventListener(LINK(this, CandidateMgr, WindowEventListener));
68 }
69 }
70
PaintTransparentChildren(vcl::Window const & rWindow,tools::Rectangle const & rPixelRect)71 void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
72 {
73 if (!rWindow.IsChildTransparentModeEnabled())
74 return;
75
76 CandidateMgr aManager;
77 aManager.PaintTransparentChildren(rWindow, rPixelRect);
78 }
79
PaintTransparentChildren(vcl::Window const & rWindow,tools::Rectangle const & rPixelRect)80 void CandidateMgr::PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
81 {
82 vcl::Window * pCandidate = rWindow.GetWindow( GetWindowType::FirstChild );
83 while (pCandidate)
84 {
85 if (pCandidate->IsPaintTransparent())
86 {
87 const tools::Rectangle aCandidatePosSizePixel(
88 pCandidate->GetPosPixel(),
89 pCandidate->GetSizePixel());
90
91 if (aCandidatePosSizePixel.Overlaps(rPixelRect))
92 {
93 m_aCandidates.emplace_back(pCandidate);
94 pCandidate->AddEventListener(LINK(this, CandidateMgr, WindowEventListener));
95 }
96 }
97 pCandidate = pCandidate->GetWindow( GetWindowType::Next );
98 }
99
100 for (const auto& rpCandidate : m_aCandidates)
101 {
102 pCandidate = rpCandidate.get();
103 if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
104 continue;
105 //rhbz#1007697 this can cause the window itself to be
106 //deleted. So we are listening to see if that happens
107 //and if so, then skip the update
108 pCandidate->Invalidate(InvalidateFlags::NoTransparent|InvalidateFlags::Children);
109 // important: actually paint the child here!
110 if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
111 continue;
112 pCandidate->PaintImmediately();
113 }
114 }
115
SdrPreRenderDevice(OutputDevice & rOriginal)116 SdrPreRenderDevice::SdrPreRenderDevice(OutputDevice& rOriginal)
117 : mpOutputDevice(&rOriginal),
118 mpPreRenderDevice(VclPtr<VirtualDevice>::Create())
119 {
120 }
121
~SdrPreRenderDevice()122 SdrPreRenderDevice::~SdrPreRenderDevice()
123 {
124 mpPreRenderDevice.disposeAndClear();
125 }
126
PreparePreRenderDevice()127 void SdrPreRenderDevice::PreparePreRenderDevice()
128 {
129 // compare size of mpPreRenderDevice with size of visible area
130 if(mpPreRenderDevice->GetOutputSizePixel() != mpOutputDevice->GetOutputSizePixel())
131 {
132 mpPreRenderDevice->SetOutputSizePixel(mpOutputDevice->GetOutputSizePixel());
133 }
134
135 // Also compare the MapModes for zoom/scroll changes
136 if(mpPreRenderDevice->GetMapMode() != mpOutputDevice->GetMapMode())
137 {
138 mpPreRenderDevice->SetMapMode(mpOutputDevice->GetMapMode());
139 }
140
141 // #i29186#
142 mpPreRenderDevice->SetDrawMode(mpOutputDevice->GetDrawMode());
143 mpPreRenderDevice->SetSettings(mpOutputDevice->GetSettings());
144 }
145
OutputPreRenderDevice(const vcl::Region & rExpandedRegion)146 void SdrPreRenderDevice::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
147 {
148 // region to pixels
149 const vcl::Region aRegionPixel(mpOutputDevice->LogicToPixel(rExpandedRegion));
150 //RegionHandle aRegionHandle(aRegionPixel.BeginEnumRects());
151 //Rectangle aRegionRectanglePixel;
152
153 // MapModes off
154 bool bMapModeWasEnabledDest(mpOutputDevice->IsMapModeEnabled());
155 bool bMapModeWasEnabledSource(mpPreRenderDevice->IsMapModeEnabled());
156 mpOutputDevice->EnableMapMode(false);
157 mpPreRenderDevice->EnableMapMode(false);
158
159 RectangleVector aRectangles;
160 aRegionPixel.GetRegionRectangles(aRectangles);
161
162 for(const auto& rRect : aRectangles)
163 {
164 // for each rectangle, copy the area
165 const Point aTopLeft(rRect.TopLeft());
166 const Size aSize(rRect.GetSize());
167
168 mpOutputDevice->DrawOutDev(
169 aTopLeft, aSize,
170 aTopLeft, aSize,
171 *mpPreRenderDevice);
172 }
173
174 mpOutputDevice->EnableMapMode(bMapModeWasEnabledDest);
175 mpPreRenderDevice->EnableMapMode(bMapModeWasEnabledSource);
176 }
177
InitOverlayManager(rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager)178 void SdrPaintView::InitOverlayManager(rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager)
179 {
180 Color aColA(SvtOptionsDrawinglayer::GetStripeColorA());
181 Color aColB(SvtOptionsDrawinglayer::GetStripeColorB());
182
183 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
184 {
185 aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor();
186 aColB.Invert();
187 }
188
189 xOverlayManager->setStripeColorA(aColA);
190 xOverlayManager->setStripeColorB(aColB);
191 xOverlayManager->setStripeLengthPixel(officecfg::Office::Common::Drawinglayer::StripeLength::get());
192 }
193
CreateOverlayManager(OutputDevice & rOutputDevice) const194 rtl::Reference<sdr::overlay::OverlayManager> SdrPaintView::CreateOverlayManager(OutputDevice& rOutputDevice) const
195 {
196 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager;
197 // is it a window?
198 if (OUTDEV_WINDOW == rOutputDevice.GetOutDevType())
199 {
200 vcl::Window* pWindow = rOutputDevice.GetOwnerWindow();
201 // decide which OverlayManager to use
202 if (IsBufferedOverlayAllowed() && !pWindow->SupportsDoubleBuffering())
203 {
204 // buffered OverlayManager, buffers its background and refreshes from there
205 // for pure overlay changes (no system redraw). The 3rd parameter specifies
206 // whether that refresh itself will use a 2nd vdev to avoid flickering.
207 // Also hand over the old OverlayManager if existent; this means to take over
208 // the registered OverlayObjects from it
209 xOverlayManager = sdr::overlay::OverlayManagerBuffered::create(rOutputDevice);
210 }
211 else
212 {
213 // unbuffered OverlayManager, just invalidates places where changes
214 // take place
215 // Also hand over the old OverlayManager if existent; this means to take over
216 // the registered OverlayObjects from it
217 xOverlayManager = sdr::overlay::OverlayManager::create(rOutputDevice);
218 }
219
220 OSL_ENSURE(xOverlayManager.is(), "SdrPaintWindow::SdrPaintWindow: Could not allocate an overlayManager (!)");
221
222 // Request a repaint so that the buffered overlay manager fills
223 // its buffer properly. This is a workaround for missing buffer
224 // updates.
225 if (!comphelper::LibreOfficeKit::isActive())
226 {
227 pWindow->Invalidate();
228 }
229
230 InitOverlayManager(xOverlayManager);
231 }
232 return xOverlayManager;
233 }
234
impCreateOverlayManager()235 void SdrPaintWindow::impCreateOverlayManager()
236 {
237 // not yet one created?
238 if(!mxOverlayManager.is())
239 mxOverlayManager = mrPaintView.CreateOverlayManager(GetOutputDevice());
240 }
241
SdrPaintWindow(SdrPaintView & rNewPaintView,OutputDevice & rOut,vcl::Window * pWindow)242 SdrPaintWindow::SdrPaintWindow(SdrPaintView& rNewPaintView, OutputDevice& rOut, vcl::Window* pWindow)
243 : mpOutputDevice(&rOut),
244 mpWindow(pWindow),
245 mrPaintView(rNewPaintView),
246 mbTemporaryTarget(false), // #i72889#
247 mbOutputToWindow(OUTDEV_WINDOW == mpOutputDevice->GetOutDevType()),
248 mpPatched(nullptr)
249 {
250 }
251
~SdrPaintWindow()252 SdrPaintWindow::~SdrPaintWindow()
253 {
254 mxOverlayManager.clear();
255
256 mpPreRenderDevice.reset();
257 }
258
GetOverlayManager() const259 rtl::Reference< sdr::overlay::OverlayManager > const & SdrPaintWindow::GetOverlayManager() const
260 {
261 if(!mxOverlayManager.is())
262 {
263 // Create buffered overlay manager by default.
264 const_cast< SdrPaintWindow* >(this)->impCreateOverlayManager();
265 }
266
267 return mxOverlayManager;
268 }
269
GetVisibleArea() const270 tools::Rectangle SdrPaintWindow::GetVisibleArea() const
271 {
272 Size aVisSizePixel(GetOutputDevice().GetOutputSizePixel());
273 return GetOutputDevice().PixelToLogic(tools::Rectangle(Point(0,0), aVisSizePixel));
274 }
275
OutputToRecordingMetaFile() const276 bool SdrPaintWindow::OutputToRecordingMetaFile() const
277 {
278 GDIMetaFile* pMetaFile = mpOutputDevice->GetConnectMetaFile();
279 return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
280 }
281
PreparePreRenderDevice()282 void SdrPaintWindow::PreparePreRenderDevice()
283 {
284 const bool bPrepareBufferedOutput(
285 mrPaintView.IsBufferedOutputAllowed()
286 && !OutputToPrinter()
287 && !mpOutputDevice->IsVirtual()
288 && !OutputToRecordingMetaFile());
289
290 if(bPrepareBufferedOutput)
291 {
292 if(!mpPreRenderDevice)
293 {
294 mpPreRenderDevice.reset(new SdrPreRenderDevice(*mpOutputDevice));
295 }
296 mpPreRenderDevice->PreparePreRenderDevice();
297 }
298 else
299 {
300 mpPreRenderDevice.reset();
301 }
302 }
303
OutputPreRenderDevice(const vcl::Region & rExpandedRegion)304 void SdrPaintWindow::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
305 {
306 if(mpPreRenderDevice)
307 {
308 mpPreRenderDevice->OutputPreRenderDevice(rExpandedRegion);
309 }
310 }
311
312 // #i73602# add flag if buffer shall be used
DrawOverlay(const vcl::Region & rRegion)313 void SdrPaintWindow::DrawOverlay(const vcl::Region& rRegion)
314 {
315 // ## force creation of OverlayManager since the first repaint needs to
316 // save the background to get a controlled start into overlay mechanism
317 impCreateOverlayManager();
318
319 if(mxOverlayManager.is() && !OutputToPrinter())
320 {
321 if(mpPreRenderDevice)
322 {
323 mxOverlayManager->completeRedraw(rRegion, &mpPreRenderDevice->GetPreRenderDevice());
324 }
325 else
326 {
327 mxOverlayManager->completeRedraw(rRegion);
328 }
329 }
330 }
331
332
SetRedrawRegion(const vcl::Region & rNew)333 void SdrPaintWindow::SetRedrawRegion(const vcl::Region& rNew)
334 {
335 maRedrawRegion = rNew;
336 }
337
338 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
339