xref: /core/svx/source/svdraw/sdrpaintwindow.cxx (revision 9a71ac68)
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