/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_FEATURE_OPENGL #include #endif // PaintBufferGuard namespace vcl { PaintBufferGuard::PaintBufferGuard(ImplFrameData* pFrameData, vcl::Window* pWindow) : mpFrameData(pFrameData), m_pWindow(pWindow), mbBackground(false), mnOutOffX(0), mnOutOffY(0) { if (!pFrameData->mpBuffer) return; // transfer various settings // FIXME: this must disappear as we move to RenderContext only, // the painting must become state-less, so that no actual // vcl::Window setting affects this mbBackground = pFrameData->mpBuffer->IsBackground(); if (pWindow->IsBackground()) { maBackground = pFrameData->mpBuffer->GetBackground(); pFrameData->mpBuffer->SetBackground(pWindow->GetBackground()); } //else //SAL_WARN("vcl.window", "the root of the double-buffering hierarchy should not have a transparent background"); vcl::PushFlags nFlags = vcl::PushFlags::NONE; nFlags |= vcl::PushFlags::CLIPREGION; nFlags |= vcl::PushFlags::FILLCOLOR; nFlags |= vcl::PushFlags::FONT; nFlags |= vcl::PushFlags::LINECOLOR; nFlags |= vcl::PushFlags::MAPMODE; maSettings = pFrameData->mpBuffer->GetSettings(); nFlags |= vcl::PushFlags::REFPOINT; nFlags |= vcl::PushFlags::TEXTCOLOR; nFlags |= vcl::PushFlags::TEXTLINECOLOR; nFlags |= vcl::PushFlags::OVERLINECOLOR; nFlags |= vcl::PushFlags::TEXTFILLCOLOR; nFlags |= vcl::PushFlags::TEXTALIGN; nFlags |= vcl::PushFlags::RASTEROP; nFlags |= vcl::PushFlags::TEXTLAYOUTMODE; nFlags |= vcl::PushFlags::TEXTLANGUAGE; pFrameData->mpBuffer->Push(nFlags); auto& rDev = *pWindow->GetOutDev(); pFrameData->mpBuffer->SetClipRegion(rDev.GetClipRegion()); pFrameData->mpBuffer->SetFillColor(rDev.GetFillColor()); pFrameData->mpBuffer->SetFont(pWindow->GetFont()); pFrameData->mpBuffer->SetLineColor(rDev.GetLineColor()); pFrameData->mpBuffer->SetMapMode(pWindow->GetMapMode()); pFrameData->mpBuffer->SetRefPoint(rDev.GetRefPoint()); pFrameData->mpBuffer->SetSettings(pWindow->GetSettings()); pFrameData->mpBuffer->SetTextColor(pWindow->GetTextColor()); pFrameData->mpBuffer->SetTextLineColor(pWindow->GetTextLineColor()); pFrameData->mpBuffer->SetOverlineColor(pWindow->GetOverlineColor()); pFrameData->mpBuffer->SetTextFillColor(pWindow->GetTextFillColor()); pFrameData->mpBuffer->SetTextAlign(pWindow->GetTextAlign()); pFrameData->mpBuffer->SetRasterOp(rDev.GetRasterOp()); pFrameData->mpBuffer->SetLayoutMode(rDev.GetLayoutMode()); pFrameData->mpBuffer->SetDigitLanguage(rDev.GetDigitLanguage()); mnOutOffX = pFrameData->mpBuffer->GetOutOffXPixel(); mnOutOffY = pFrameData->mpBuffer->GetOutOffYPixel(); pFrameData->mpBuffer->SetOutOffXPixel(pWindow->GetOutOffXPixel()); pFrameData->mpBuffer->SetOutOffYPixel(pWindow->GetOutOffYPixel()); pFrameData->mpBuffer->EnableRTL(pWindow->IsRTLEnabled()); } PaintBufferGuard::~PaintBufferGuard() COVERITY_NOEXCEPT_FALSE { if (!mpFrameData->mpBuffer) return; if (!m_aPaintRect.IsEmpty()) { // copy the buffer content to the actual window // export VCL_DOUBLEBUFFERING_AVOID_PAINT=1 to see where we are // painting directly instead of using Invalidate() // [ie. everything you can see was painted directly to the // window either above or in eg. an event handler] if (!getenv("VCL_DOUBLEBUFFERING_AVOID_PAINT")) { // Make sure that the +1 value GetSize() adds to the size is in pixels. Size aPaintRectSize; if (m_pWindow->GetMapMode().GetMapUnit() == MapUnit::MapPixel) { aPaintRectSize = m_aPaintRect.GetSize(); } else { tools::Rectangle aRectanglePixel = m_pWindow->LogicToPixel(m_aPaintRect); aPaintRectSize = m_pWindow->PixelToLogic(aRectanglePixel.GetSize()); } m_pWindow->GetOutDev()->DrawOutDev(m_aPaintRect.TopLeft(), aPaintRectSize, m_aPaintRect.TopLeft(), aPaintRectSize, *mpFrameData->mpBuffer); } } // Restore buffer state. mpFrameData->mpBuffer->SetOutOffXPixel(mnOutOffX); mpFrameData->mpBuffer->SetOutOffYPixel(mnOutOffY); mpFrameData->mpBuffer->Pop(); mpFrameData->mpBuffer->SetSettings(maSettings); if (mbBackground) mpFrameData->mpBuffer->SetBackground(maBackground); else mpFrameData->mpBuffer->SetBackground(); } void PaintBufferGuard::SetPaintRect(const tools::Rectangle& rRectangle) { m_aPaintRect = rRectangle; } vcl::RenderContext* PaintBufferGuard::GetRenderContext() { if (mpFrameData->mpBuffer) return mpFrameData->mpBuffer; else return m_pWindow->GetOutDev(); } } class PaintHelper { private: VclPtr m_pWindow; std::unique_ptr m_pChildRegion; tools::Rectangle m_aSelectionRect; tools::Rectangle m_aPaintRect; vcl::Region m_aPaintRegion; ImplPaintFlags m_nPaintFlags; bool m_bPop : 1; bool m_bRestoreCursor : 1; bool m_bStartedBufferedPaint : 1; ///< This PaintHelper started a buffered paint, and should paint it on the screen when being destructed. public: PaintHelper(vcl::Window* pWindow, ImplPaintFlags nPaintFlags); void SetPop() { m_bPop = true; } void SetPaintRect(const tools::Rectangle& rRect) { m_aPaintRect = rRect; } void SetSelectionRect(const tools::Rectangle& rRect) { m_aSelectionRect = rRect; } void SetRestoreCursor(bool bRestoreCursor) { m_bRestoreCursor = bRestoreCursor; } bool GetRestoreCursor() const { return m_bRestoreCursor; } ImplPaintFlags GetPaintFlags() const { return m_nPaintFlags; } vcl::Region& GetPaintRegion() { return m_aPaintRegion; } void DoPaint(const vcl::Region* pRegion); /// Start buffered paint: set it up to have the same settings as m_pWindow. void StartBufferedPaint(); /// Paint the content of the buffer to the current m_pWindow. void PaintBuffer(); ~PaintHelper(); }; PaintHelper::PaintHelper(vcl::Window *pWindow, ImplPaintFlags nPaintFlags) : m_pWindow(pWindow) , m_nPaintFlags(nPaintFlags) , m_bPop(false) , m_bRestoreCursor(false) , m_bStartedBufferedPaint(false) { } void PaintHelper::StartBufferedPaint() { ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData; assert(!pFrameData->mbInBufferedPaint); pFrameData->mbInBufferedPaint = true; pFrameData->maBufferedRect = tools::Rectangle(); m_bStartedBufferedPaint = true; } void PaintHelper::PaintBuffer() { ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData; assert(pFrameData->mbInBufferedPaint); assert(m_bStartedBufferedPaint); vcl::PaintBufferGuard aGuard(pFrameData, m_pWindow); aGuard.SetPaintRect(pFrameData->maBufferedRect); } void PaintHelper::DoPaint(const vcl::Region* pRegion) { WindowImpl* pWindowImpl = m_pWindow->ImplGetWindowImpl(); vcl::Region& rWinChildClipRegion = m_pWindow->ImplGetWinChildClipRegion(); ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData; if (pWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll || pFrameData->mbInBufferedPaint) { pWindowImpl->maInvalidateRegion = rWinChildClipRegion; } else { if (pRegion) pWindowImpl->maInvalidateRegion.Union( *pRegion ); if (pWindowImpl->mpWinData && pWindowImpl->mbTrackVisible) /* #98602# need to repaint all children within the * tracking rectangle, so the following invert * operation takes places without traces of the previous * one. */ pWindowImpl->maInvalidateRegion.Union(*pWindowImpl->mpWinData->mpTrackRect); if (pWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren) m_pChildRegion.reset( new vcl::Region(pWindowImpl->maInvalidateRegion) ); pWindowImpl->maInvalidateRegion.Intersect(rWinChildClipRegion); } pWindowImpl->mnPaintFlags = ImplPaintFlags::NONE; if (pWindowImpl->maInvalidateRegion.IsEmpty()) return; #if HAVE_FEATURE_OPENGL VCL_GL_INFO("PaintHelper::DoPaint on " << typeid( *m_pWindow ).name() << " '" << m_pWindow->GetText() << "' begin"); #endif // double-buffering: setup the buffer if it does not exist if (!pFrameData->mbInBufferedPaint && m_pWindow->SupportsDoubleBuffering()) StartBufferedPaint(); // double-buffering: if this window does not support double-buffering, // but we are in the middle of double-buffered paint, we might be // losing information if (pFrameData->mbInBufferedPaint && !m_pWindow->SupportsDoubleBuffering()) SAL_WARN("vcl.window", "non-double buffered window in the double-buffered hierarchy, painting directly: " << typeid(*m_pWindow.get()).name()); if (pFrameData->mbInBufferedPaint && m_pWindow->SupportsDoubleBuffering()) { // double-buffering vcl::PaintBufferGuard g(pFrameData, m_pWindow); m_pWindow->ApplySettings(*pFrameData->mpBuffer); m_pWindow->PushPaintHelper(this, *pFrameData->mpBuffer); m_pWindow->Paint(*pFrameData->mpBuffer, m_aPaintRect); pFrameData->maBufferedRect.Union(m_aPaintRect); } else { // direct painting Wallpaper aBackground = m_pWindow->GetBackground(); m_pWindow->ApplySettings(*m_pWindow->GetOutDev()); // Restore bitmap background if it was lost. if (aBackground.IsBitmap() && !m_pWindow->GetBackground().IsBitmap()) { m_pWindow->SetBackground(aBackground); } m_pWindow->PushPaintHelper(this, *m_pWindow->GetOutDev()); m_pWindow->Paint(*m_pWindow->GetOutDev(), m_aPaintRect); } #if HAVE_FEATURE_OPENGL VCL_GL_INFO("PaintHelper::DoPaint end on " << typeid( *m_pWindow ).name() << " '" << m_pWindow->GetText() << "'"); #endif } namespace vcl { void RenderTools::DrawSelectionBackground(vcl::RenderContext& rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& rRect, sal_uInt16 nHighlight, bool bChecked, bool bDrawBorder, bool bDrawExtBorderOnly, Color* pSelectionTextColor, tools::Long nCornerRadius, Color const * pPaintColor) { if (rRect.IsEmpty()) return; bool bRoundEdges = nCornerRadius > 0; const StyleSettings& rStyles = rRenderContext.GetSettings().GetStyleSettings(); // colors used for item highlighting Color aSelectionBorderColor(pPaintColor ? *pPaintColor : rStyles.GetHighlightColor()); Color aSelectionFillColor(aSelectionBorderColor); bool bDark = rStyles.GetFaceColor().IsDark(); bool bBright = ( rStyles.GetFaceColor() == COL_WHITE ); int c1 = aSelectionBorderColor.GetLuminance(); int c2 = rWindow.GetBackgroundColor().GetLuminance(); if (!bDark && !bBright && std::abs(c2 - c1) < (pPaintColor ? 40 : 75)) { // contrast too low sal_uInt16 h, s, b; aSelectionFillColor.RGBtoHSB( h, s, b ); if( b > 50 ) b -= 40; else b += 40; aSelectionFillColor = Color::HSBtoRGB( h, s, b ); aSelectionBorderColor = aSelectionFillColor; } if (bRoundEdges) { if (aSelectionBorderColor.IsDark()) aSelectionBorderColor.IncreaseLuminance(128); else aSelectionBorderColor.DecreaseLuminance(128); } tools::Rectangle aRect(rRect); if (bDrawExtBorderOnly) { aRect.AdjustLeft( -1 ); aRect.AdjustTop( -1 ); aRect.AdjustRight(1 ); aRect.AdjustBottom(1 ); } rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); if (bDrawBorder) rRenderContext.SetLineColor(bDark ? COL_WHITE : (bBright ? COL_BLACK : aSelectionBorderColor)); else rRenderContext.SetLineColor(); sal_uInt16 nPercent = 0; if (!nHighlight) { if (bDark) aSelectionFillColor = COL_BLACK; else nPercent = 80; // just checked (light) } else { if (bChecked && nHighlight == 2) { if (bDark) aSelectionFillColor = COL_LIGHTGRAY; else if (bBright) { aSelectionFillColor = COL_BLACK; rRenderContext.SetLineColor(COL_BLACK); nPercent = 0; } else nPercent = bRoundEdges ? 40 : 20; // selected, pressed or checked ( very dark ) } else if (bChecked || nHighlight == 1) { if (bDark) aSelectionFillColor = COL_GRAY; else if (bBright) { aSelectionFillColor = COL_BLACK; rRenderContext.SetLineColor(COL_BLACK); nPercent = 0; } else nPercent = bRoundEdges ? 60 : 35; // selected, pressed or checked ( very dark ) } else { if (bDark) aSelectionFillColor = COL_LIGHTGRAY; else if (bBright) { aSelectionFillColor = COL_BLACK; rRenderContext.SetLineColor(COL_BLACK); if (nHighlight == 3) nPercent = 80; else nPercent = 0; } else nPercent = 70; // selected ( dark ) } } if (bDark && bDrawExtBorderOnly) { rRenderContext.SetFillColor(); if (pSelectionTextColor) *pSelectionTextColor = rStyles.GetHighlightTextColor(); } else { rRenderContext.SetFillColor(aSelectionFillColor); if (pSelectionTextColor) { Color aTextColor = rWindow.IsControlBackground() ? rWindow.GetControlForeground() : rStyles.GetButtonTextColor(); Color aHLTextColor = rStyles.GetHighlightTextColor(); int nTextDiff = std::abs(aSelectionFillColor.GetLuminance() - aTextColor.GetLuminance()); int nHLDiff = std::abs(aSelectionFillColor.GetLuminance() - aHLTextColor.GetLuminance()); *pSelectionTextColor = (nHLDiff >= nTextDiff) ? aHLTextColor : aTextColor; } } if (bDark) { rRenderContext.DrawRect(aRect); } else { if (bRoundEdges) { tools::Polygon aPoly(aRect, nCornerRadius, nCornerRadius); tools::PolyPolygon aPolyPoly(aPoly); rRenderContext.DrawTransparent(aPolyPoly, nPercent); } else { tools::Polygon aPoly(aRect); tools::PolyPolygon aPolyPoly(aPoly); rRenderContext.DrawTransparent(aPolyPoly, nPercent); } } rRenderContext.Pop(); // LINECOLOR | FILLCOLOR } void Window::PushPaintHelper(PaintHelper *pHelper, vcl::RenderContext& rRenderContext) { pHelper->SetPop(); if ( mpWindowImpl->mpCursor ) pHelper->SetRestoreCursor(mpWindowImpl->mpCursor->ImplSuspend()); GetOutDev()->mbInitClipRegion = true; mpWindowImpl->mbInPaint = true; // restore Paint-Region vcl::Region &rPaintRegion = pHelper->GetPaintRegion(); rPaintRegion = mpWindowImpl->maInvalidateRegion; tools::Rectangle aPaintRect = rPaintRegion.GetBoundRect(); // RTL: re-mirror paint rect and region at this window if (GetOutDev()->ImplIsAntiparallel()) { rRenderContext.ReMirror(aPaintRect); rRenderContext.ReMirror(rPaintRegion); } aPaintRect = GetOutDev()->ImplDevicePixelToLogic(aPaintRect); mpWindowImpl->mpPaintRegion = &rPaintRegion; mpWindowImpl->maInvalidateRegion.SetEmpty(); if ((pHelper->GetPaintFlags() & ImplPaintFlags::Erase) && rRenderContext.IsBackground()) { if (rRenderContext.IsClipRegion()) { vcl::Region aOldRegion = rRenderContext.GetClipRegion(); rRenderContext.SetClipRegion(); Erase(rRenderContext); rRenderContext.SetClipRegion(aOldRegion); } else Erase(rRenderContext); } // #98943# trigger drawing of toolbox selection after all children are painted if (mpWindowImpl->mbDrawSelectionBackground) pHelper->SetSelectionRect(aPaintRect); pHelper->SetPaintRect(aPaintRect); } void Window::PopPaintHelper(PaintHelper const *pHelper) { if (mpWindowImpl->mpWinData) { if (mpWindowImpl->mbFocusVisible) ImplInvertFocus(*mpWindowImpl->mpWinData->mpFocusRect); } mpWindowImpl->mbInPaint = false; GetOutDev()->mbInitClipRegion = true; mpWindowImpl->mpPaintRegion = nullptr; if (mpWindowImpl->mpCursor) mpWindowImpl->mpCursor->ImplResume(pHelper->GetRestoreCursor()); } } /* namespace vcl */ PaintHelper::~PaintHelper() { WindowImpl* pWindowImpl = m_pWindow->ImplGetWindowImpl(); if (m_bPop) { m_pWindow->PopPaintHelper(this); } ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData; if ( m_nPaintFlags & (ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren) ) { // Paint from the bottom child window and frontward. vcl::Window* pTempWindow = pWindowImpl->mpLastChild; while (pTempWindow) { if (pTempWindow->mpWindowImpl->mbVisible) pTempWindow->ImplCallPaint(m_pChildRegion.get(), m_nPaintFlags); pTempWindow = pTempWindow->mpWindowImpl->mpPrev; } } if ( pWindowImpl->mpWinData && pWindowImpl->mbTrackVisible && (pWindowImpl->mpWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) ) /* #98602# need to invert the tracking rect AFTER * the children have painted */ m_pWindow->InvertTracking( *pWindowImpl->mpWinData->mpTrackRect, pWindowImpl->mpWinData->mnTrackFlags ); // double-buffering: paint in case we created the buffer, the children are // already painted inside if (m_bStartedBufferedPaint && pFrameData->mbInBufferedPaint) { PaintBuffer(); pFrameData->mbInBufferedPaint = false; pFrameData->maBufferedRect = tools::Rectangle(); } // #98943# draw toolbox selection if( !m_aSelectionRect.IsEmpty() ) m_pWindow->DrawSelectionBackground( m_aSelectionRect, 3, false, true ); } namespace vcl { void Window::ImplCallPaint(const vcl::Region* pRegion, ImplPaintFlags nPaintFlags) { // call PrePaint. PrePaint may add to the invalidate region as well as // other parameters used below. PrePaint(*GetOutDev()); mpWindowImpl->mbPaintFrame = false; if (nPaintFlags & ImplPaintFlags::PaintAllChildren) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Paint | ImplPaintFlags::PaintAllChildren | (nPaintFlags & ImplPaintFlags::PaintAll); if (nPaintFlags & ImplPaintFlags::PaintChildren) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintChildren; if (nPaintFlags & ImplPaintFlags::Erase) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Erase; if (nPaintFlags & ImplPaintFlags::CheckRtl) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::CheckRtl; if (!mpWindowImpl->mpFirstChild) mpWindowImpl->mnPaintFlags &= ~ImplPaintFlags::PaintAllChildren; // If tiled rendering is used, windows are only invalidated, never painted to. if (mpWindowImpl->mbPaintDisabled || comphelper::LibreOfficeKit::isActive()) { if (mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll) Invalidate(InvalidateFlags::NoChildren | InvalidateFlags::NoErase | InvalidateFlags::NoTransparent | InvalidateFlags::NoClipChildren); else if ( pRegion ) Invalidate(*pRegion, InvalidateFlags::NoChildren | InvalidateFlags::NoErase | InvalidateFlags::NoTransparent | InvalidateFlags::NoClipChildren); // call PostPaint before returning PostPaint(*GetOutDev()); return; } nPaintFlags = mpWindowImpl->mnPaintFlags & ~ImplPaintFlags::Paint; PaintHelper aHelper(this, nPaintFlags); if (mpWindowImpl->mnPaintFlags & ImplPaintFlags::Paint) aHelper.DoPaint(pRegion); else mpWindowImpl->mnPaintFlags = ImplPaintFlags::NONE; // call PostPaint PostPaint(*GetOutDev()); } void Window::ImplCallOverlapPaint() { if (!mpWindowImpl) return; // emit overlapping windows first vcl::Window* pTempWindow = mpWindowImpl->mpFirstOverlap; while ( pTempWindow ) { if ( pTempWindow->mpWindowImpl->mbReallyVisible ) pTempWindow->ImplCallOverlapPaint(); pTempWindow = pTempWindow->mpWindowImpl->mpNext; } // only then ourself if ( mpWindowImpl->mnPaintFlags & (ImplPaintFlags::Paint | ImplPaintFlags::PaintChildren) ) { // RTL: notify ImplCallPaint to check for re-mirroring // because we were called from the Sal layer ImplCallPaint(nullptr, mpWindowImpl->mnPaintFlags /*| ImplPaintFlags::CheckRtl */); } } IMPL_LINK_NOARG(Window, ImplHandlePaintHdl, Timer *, void) { comphelper::ProfileZone aZone("VCL idle re-paint"); // save paint events until layout is done if (IsSystemWindow() && static_cast(this)->hasPendingLayout()) { mpWindowImpl->mpFrameData->maPaintIdle.Start(); return; } // save paint events until resizing or initial sizing done if (mpWindowImpl->mbFrame && mpWindowImpl->mpFrameData->maResizeIdle.IsActive()) { mpWindowImpl->mpFrameData->maPaintIdle.Start(); } else if ( mpWindowImpl->mbReallyVisible ) { ImplCallOverlapPaint(); if (comphelper::LibreOfficeKit::isActive() && mpWindowImpl->mpFrameData->maPaintIdle.IsActive()) mpWindowImpl->mpFrameData->maPaintIdle.Stop(); } } IMPL_LINK_NOARG(Window, ImplHandleResizeTimerHdl, Timer *, void) { comphelper::ProfileZone aZone("VCL idle resize"); if( mpWindowImpl->mbReallyVisible ) { ImplCallResize(); if( mpWindowImpl->mpFrameData->maPaintIdle.IsActive() ) { mpWindowImpl->mpFrameData->maPaintIdle.Stop(); mpWindowImpl->mpFrameData->maPaintIdle.Invoke( nullptr ); } } } void Window::ImplInvalidateFrameRegion( const vcl::Region* pRegion, InvalidateFlags nFlags ) { // set PAINTCHILDREN for all parent windows till the first OverlapWindow if ( !ImplIsOverlapWindow() ) { vcl::Window* pTempWindow = this; ImplPaintFlags nTranspPaint = IsPaintTransparent() ? ImplPaintFlags::Paint : ImplPaintFlags::NONE; do { pTempWindow = pTempWindow->ImplGetParent(); if ( pTempWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintChildren ) break; pTempWindow->mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintChildren | nTranspPaint; if( ! pTempWindow->IsPaintTransparent() ) nTranspPaint = ImplPaintFlags::NONE; } while ( !pTempWindow->ImplIsOverlapWindow() ); } // set Paint-Flags mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Paint; if ( nFlags & InvalidateFlags::Children ) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintAllChildren; if ( !(nFlags & InvalidateFlags::NoErase) ) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Erase; if ( !pRegion ) mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintAll; else if ( !(mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll) ) { // if not everything has to be redrawn, add the region to it mpWindowImpl->maInvalidateRegion.Union( *pRegion ); } // Handle transparent windows correctly: invalidate must be done on the first opaque parent if( ((IsPaintTransparent() && !(nFlags & InvalidateFlags::NoTransparent)) || (nFlags & InvalidateFlags::Transparent) ) && ImplGetParent() ) { vcl::Window *pParent = ImplGetParent(); while( pParent && pParent->IsPaintTransparent() ) pParent = pParent->ImplGetParent(); if( pParent ) { vcl::Region *pChildRegion; if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll ) // invalidate the whole child window region in the parent pChildRegion = &ImplGetWinChildClipRegion(); else // invalidate the same region in the parent that has to be repainted in the child pChildRegion = &mpWindowImpl->maInvalidateRegion; nFlags |= InvalidateFlags::Children; // paint should also be done on all children nFlags &= ~InvalidateFlags::NoErase; // parent should paint and erase to create proper background pParent->ImplInvalidateFrameRegion( pChildRegion, nFlags ); } } if ( !mpWindowImpl->mpFrameData->maPaintIdle.IsActive() ) mpWindowImpl->mpFrameData->maPaintIdle.Start(); } void Window::ImplInvalidateOverlapFrameRegion( const vcl::Region& rRegion ) { vcl::Region aRegion = rRegion; ImplClipBoundaries( aRegion, true, true ); if ( !aRegion.IsEmpty() ) ImplInvalidateFrameRegion( &aRegion, InvalidateFlags::Children ); // now we invalidate the overlapping windows vcl::Window* pTempWindow = mpWindowImpl->mpFirstOverlap; while ( pTempWindow ) { if ( pTempWindow->IsVisible() ) pTempWindow->ImplInvalidateOverlapFrameRegion( rRegion ); pTempWindow = pTempWindow->mpWindowImpl->mpNext; } } void Window::ImplInvalidateParentFrameRegion( const vcl::Region& rRegion ) { if ( mpWindowImpl->mbOverlapWin ) mpWindowImpl->mpFrameWindow->ImplInvalidateOverlapFrameRegion( rRegion ); else { if( ImplGetParent() ) ImplGetParent()->ImplInvalidateFrameRegion( &rRegion, InvalidateFlags::Children ); } } void Window::ImplInvalidate( const vcl::Region* pRegion, InvalidateFlags nFlags ) { // check what has to be redrawn bool bInvalidateAll = !pRegion; // take Transparent-Invalidate into account vcl::Window* pOpaqueWindow = this; if ( (mpWindowImpl->mbPaintTransparent && !(nFlags & InvalidateFlags::NoTransparent)) || (nFlags & InvalidateFlags::Transparent) ) { vcl::Window* pTempWindow = pOpaqueWindow->ImplGetParent(); while ( pTempWindow ) { if ( !pTempWindow->IsPaintTransparent() ) { pOpaqueWindow = pTempWindow; nFlags |= InvalidateFlags::Children; bInvalidateAll = false; break; } if ( pTempWindow->ImplIsOverlapWindow() ) break; pTempWindow = pTempWindow->ImplGetParent(); } } // assemble region InvalidateFlags nOrgFlags = nFlags; if ( !(nFlags & (InvalidateFlags::Children | InvalidateFlags::NoChildren)) ) { if ( GetStyle() & WB_CLIPCHILDREN ) nFlags |= InvalidateFlags::NoChildren; else nFlags |= InvalidateFlags::Children; } if ( (nFlags & InvalidateFlags::NoChildren) && mpWindowImpl->mpFirstChild ) bInvalidateAll = false; if ( bInvalidateAll ) ImplInvalidateFrameRegion( nullptr, nFlags ); else { vcl::Region aRegion( GetOutputRectPixel() ); if ( pRegion ) { // RTL: remirror region before intersecting it if ( GetOutDev()->ImplIsAntiparallel() ) { const OutputDevice *pOutDev = GetOutDev(); vcl::Region aRgn( *pRegion ); pOutDev->ReMirror( aRgn ); aRegion.Intersect( aRgn ); } else aRegion.Intersect( *pRegion ); } ImplClipBoundaries( aRegion, true, true ); if ( nFlags & InvalidateFlags::NoChildren ) { nFlags &= ~InvalidateFlags::Children; if ( !(nFlags & InvalidateFlags::NoClipChildren) ) { if ( nOrgFlags & InvalidateFlags::NoChildren ) ImplClipAllChildren( aRegion ); else { if ( ImplClipChildren( aRegion ) ) nFlags |= InvalidateFlags::Children; } } } if ( !aRegion.IsEmpty() ) ImplInvalidateFrameRegion( &aRegion, nFlags ); // transparency is handled here, pOpaqueWindow not required } if ( nFlags & InvalidateFlags::Update ) pOpaqueWindow->PaintImmediately(); // start painting at the opaque parent } void Window::ImplMoveInvalidateRegion( const tools::Rectangle& rRect, tools::Long nHorzScroll, tools::Long nVertScroll, bool bChildren ) { if ( (mpWindowImpl->mnPaintFlags & (ImplPaintFlags::Paint | ImplPaintFlags::PaintAll)) == ImplPaintFlags::Paint ) { vcl::Region aTempRegion = mpWindowImpl->maInvalidateRegion; aTempRegion.Intersect( rRect ); aTempRegion.Move( nHorzScroll, nVertScroll ); mpWindowImpl->maInvalidateRegion.Union( aTempRegion ); } if ( bChildren && (mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintChildren) ) { vcl::Window* pWindow = mpWindowImpl->mpFirstChild; while ( pWindow ) { pWindow->ImplMoveInvalidateRegion( rRect, nHorzScroll, nVertScroll, true ); pWindow = pWindow->mpWindowImpl->mpNext; } } } void Window::ImplMoveAllInvalidateRegions( const tools::Rectangle& rRect, tools::Long nHorzScroll, tools::Long nVertScroll, bool bChildren ) { // also shift Paint-Region when paints need processing ImplMoveInvalidateRegion( rRect, nHorzScroll, nVertScroll, bChildren ); // Paint-Region should be shifted, as drawn by the parents if ( ImplIsOverlapWindow() ) return; vcl::Region aPaintAllRegion; vcl::Window* pPaintAllWindow = this; do { pPaintAllWindow = pPaintAllWindow->ImplGetParent(); if ( pPaintAllWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren ) { if ( pPaintAllWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll ) { aPaintAllRegion.SetEmpty(); break; } else aPaintAllRegion.Union( pPaintAllWindow->mpWindowImpl->maInvalidateRegion ); } } while ( !pPaintAllWindow->ImplIsOverlapWindow() ); if ( !aPaintAllRegion.IsEmpty() ) { aPaintAllRegion.Move( nHorzScroll, nVertScroll ); InvalidateFlags nPaintFlags = InvalidateFlags::NONE; if ( bChildren ) nPaintFlags |= InvalidateFlags::Children; ImplInvalidateFrameRegion( &aPaintAllRegion, nPaintFlags ); } } void Window::ImplValidateFrameRegion( const vcl::Region* pRegion, ValidateFlags nFlags ) { if ( !pRegion ) mpWindowImpl->maInvalidateRegion.SetEmpty(); else { // when all child windows have to be drawn we need to invalidate them before doing so if ( (mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren) && mpWindowImpl->mpFirstChild ) { vcl::Region aChildRegion = mpWindowImpl->maInvalidateRegion; if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll ) { aChildRegion = GetOutputRectPixel(); } vcl::Window* pChild = mpWindowImpl->mpFirstChild; while ( pChild ) { pChild->Invalidate( aChildRegion, InvalidateFlags::Children | InvalidateFlags::NoTransparent ); pChild = pChild->mpWindowImpl->mpNext; } } if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll ) { mpWindowImpl->maInvalidateRegion = GetOutputRectPixel(); } mpWindowImpl->maInvalidateRegion.Exclude( *pRegion ); } mpWindowImpl->mnPaintFlags &= ~ImplPaintFlags::PaintAll; if ( nFlags & ValidateFlags::Children ) { vcl::Window* pChild = mpWindowImpl->mpFirstChild; while ( pChild ) { pChild->ImplValidateFrameRegion( pRegion, nFlags ); pChild = pChild->mpWindowImpl->mpNext; } } } void Window::ImplValidate() { // assemble region bool bValidateAll = true; ValidateFlags nFlags = ValidateFlags::NONE; if ( GetStyle() & WB_CLIPCHILDREN ) nFlags |= ValidateFlags::NoChildren; else nFlags |= ValidateFlags::Children; if ( (nFlags & ValidateFlags::NoChildren) && mpWindowImpl->mpFirstChild ) bValidateAll = false; if ( bValidateAll ) ImplValidateFrameRegion( nullptr, nFlags ); else { vcl::Region aRegion( GetOutputRectPixel() ); ImplClipBoundaries( aRegion, true, true ); if ( nFlags & ValidateFlags::NoChildren ) { nFlags &= ~ValidateFlags::Children; if ( ImplClipChildren( aRegion ) ) nFlags |= ValidateFlags::Children; } if ( !aRegion.IsEmpty() ) ImplValidateFrameRegion( &aRegion, nFlags ); } } void Window::ImplUpdateAll() { if ( !mpWindowImpl || !mpWindowImpl->mbReallyVisible ) return; bool bFlush = false; if ( mpWindowImpl->mpFrameWindow->mpWindowImpl->mbPaintFrame ) { Point aPoint( 0, 0 ); vcl::Region aRegion( tools::Rectangle( aPoint, GetOutputSizePixel() ) ); ImplInvalidateOverlapFrameRegion( aRegion ); if ( mpWindowImpl->mbFrame || (mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame) ) bFlush = true; } // an update changes the OverlapWindow, such that for later paints // not too much has to be drawn, if ALLCHILDREN etc. is set vcl::Window* pWindow = ImplGetFirstOverlapWindow(); pWindow->ImplCallOverlapPaint(); if ( bFlush ) GetOutDev()->Flush(); } void Window::PrePaint(vcl::RenderContext& /*rRenderContext*/) { } void Window::PostPaint(vcl::RenderContext& /*rRenderContext*/) { } void Window::Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect) { CallEventListeners(VclEventId::WindowPaint, const_cast(&rRect)); } void Window::SetPaintTransparent( bool bTransparent ) { // transparency is not useful for frames as the background would have to be provided by a different frame if( bTransparent && mpWindowImpl->mbFrame ) return; if ( mpWindowImpl->mpBorderWindow ) mpWindowImpl->mpBorderWindow->SetPaintTransparent( bTransparent ); mpWindowImpl->mbPaintTransparent = bTransparent; } void Window::SetWindowRegionPixel() { if ( mpWindowImpl->mpBorderWindow ) mpWindowImpl->mpBorderWindow->SetWindowRegionPixel(); else if( mpWindowImpl->mbFrame ) { mpWindowImpl->maWinRegion = vcl::Region(true); mpWindowImpl->mbWinRegion = false; mpWindowImpl->mpFrame->ResetClipRegion(); } else { if ( mpWindowImpl->mbWinRegion ) { mpWindowImpl->maWinRegion = vcl::Region(true); mpWindowImpl->mbWinRegion = false; ImplSetClipFlag(); if ( IsReallyVisible() ) { vcl::Region aRegion( GetOutputRectPixel() ); ImplInvalidateParentFrameRegion( aRegion ); } } } } void Window::SetWindowRegionPixel( const vcl::Region& rRegion ) { if ( mpWindowImpl->mpBorderWindow ) mpWindowImpl->mpBorderWindow->SetWindowRegionPixel( rRegion ); else if( mpWindowImpl->mbFrame ) { if( !rRegion.IsNull() ) { mpWindowImpl->maWinRegion = rRegion; mpWindowImpl->mbWinRegion = ! rRegion.IsEmpty(); if( mpWindowImpl->mbWinRegion ) { // set/update ClipRegion RectangleVector aRectangles; mpWindowImpl->maWinRegion.GetRegionRectangles(aRectangles); mpWindowImpl->mpFrame->BeginSetClipRegion(aRectangles.size()); for (auto const& rectangle : aRectangles) { mpWindowImpl->mpFrame->UnionClipRegion( rectangle.Left(), rectangle.Top(), rectangle.GetWidth(), // orig nWidth was ((R - L) + 1), same as GetWidth does rectangle.GetHeight()); // same for height } mpWindowImpl->mpFrame->EndSetClipRegion(); } else SetWindowRegionPixel(); } else SetWindowRegionPixel(); } else { if ( rRegion.IsNull() ) { if ( mpWindowImpl->mbWinRegion ) { mpWindowImpl->maWinRegion = vcl::Region(true); mpWindowImpl->mbWinRegion = false; ImplSetClipFlag(); } } else { mpWindowImpl->maWinRegion = rRegion; mpWindowImpl->mbWinRegion = true; ImplSetClipFlag(); } if ( IsReallyVisible() ) { vcl::Region aRegion( GetOutputRectPixel() ); ImplInvalidateParentFrameRegion( aRegion ); } } } vcl::Region Window::GetPaintRegion() const { if ( mpWindowImpl->mpPaintRegion ) { vcl::Region aRegion = *mpWindowImpl->mpPaintRegion; aRegion.Move( -GetOutDev()->mnOutOffX, -GetOutDev()->mnOutOffY ); return PixelToLogic( aRegion ); } else { vcl::Region aPaintRegion(true); return aPaintRegion; } } void Window::Invalidate( InvalidateFlags nFlags ) { if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) ) return; ImplInvalidate( nullptr, nFlags ); LogicInvalidate(nullptr); } void Window::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nFlags ) { if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) ) return; OutputDevice *pOutDev = GetOutDev(); tools::Rectangle aRect = pOutDev->ImplLogicToDevicePixel( rRect ); if ( !aRect.IsEmpty() ) { vcl::Region aRegion( aRect ); ImplInvalidate( &aRegion, nFlags ); tools::Rectangle aLogicRectangle(rRect); LogicInvalidate(&aLogicRectangle); } } void Window::Invalidate( const vcl::Region& rRegion, InvalidateFlags nFlags ) { if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) ) return; if ( rRegion.IsNull() ) { ImplInvalidate( nullptr, nFlags ); LogicInvalidate(nullptr); } else { vcl::Region aRegion = GetOutDev()->ImplPixelToDevicePixel( LogicToPixel( rRegion ) ); if ( !aRegion.IsEmpty() ) { ImplInvalidate( &aRegion, nFlags ); tools::Rectangle aLogicRectangle = rRegion.GetBoundRect(); LogicInvalidate(&aLogicRectangle); } } } void Window::LogicInvalidate(const tools::Rectangle* pRectangle) { if(pRectangle) { tools::Rectangle aRect = GetOutDev()->ImplLogicToDevicePixel( *pRectangle ); PixelInvalidate(&aRect); } else PixelInvalidate(nullptr); } bool Window::InvalidateByForeignEditView(EditView* ) { return false; } void Window::PixelInvalidate(const tools::Rectangle* pRectangle) { if (comphelper::LibreOfficeKit::isDialogPainting() || !comphelper::LibreOfficeKit::isActive()) return; Size aSize = GetSizePixel(); if (aSize.IsEmpty()) return; if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier()) { // In case we are routing the window, notify the client std::vector aPayload; tools::Rectangle aRect(Point(0, 0), aSize); if (pRectangle) aRect = *pRectangle; if (IsRTLEnabled() && GetOutDev() && !GetOutDev()->ImplIsAntiparallel()) GetOutDev()->ReMirror(aRect); aPayload.emplace_back("rectangle", aRect.toString()); pNotifier->notifyWindow(GetLOKWindowId(), u"invalidate"_ustr, aPayload); } // Added for dialog items. Pass invalidation to the parent window. else if (VclPtr pParent = GetParentWithLOKNotifier()) { const tools::Rectangle aRect(Point(GetOutOffXPixel(), GetOutOffYPixel()), GetSizePixel()); pParent->PixelInvalidate(&aRect); } } void Window::Validate() { if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) ) return; ImplValidate(); } bool Window::HasPaintEvent() const { if ( !mpWindowImpl->mbReallyVisible ) return false; if ( mpWindowImpl->mpFrameWindow->mpWindowImpl->mbPaintFrame ) return true; if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::Paint ) return true; if ( !ImplIsOverlapWindow() ) { const vcl::Window* pTempWindow = this; do { pTempWindow = pTempWindow->ImplGetParent(); if ( pTempWindow->mpWindowImpl->mnPaintFlags & (ImplPaintFlags::PaintChildren | ImplPaintFlags::PaintAllChildren) ) return true; } while ( !pTempWindow->ImplIsOverlapWindow() ); } return false; } void Window::PaintImmediately() { if (!mpWindowImpl) return; if ( mpWindowImpl->mpBorderWindow ) { mpWindowImpl->mpBorderWindow->PaintImmediately(); return; } if ( !mpWindowImpl->mbReallyVisible ) return; bool bFlush = false; if ( mpWindowImpl->mpFrameWindow->mpWindowImpl->mbPaintFrame ) { Point aPoint( 0, 0 ); vcl::Region aRegion( tools::Rectangle( aPoint, GetOutputSizePixel() ) ); ImplInvalidateOverlapFrameRegion( aRegion ); if ( mpWindowImpl->mbFrame || (mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame) ) bFlush = true; } // First we should skip all windows which are Paint-Transparent vcl::Window* pUpdateWindow = this; vcl::Window* pWindow = pUpdateWindow; while ( !pWindow->ImplIsOverlapWindow() ) { if ( !pWindow->mpWindowImpl->mbPaintTransparent ) { pUpdateWindow = pWindow; break; } pWindow = pWindow->ImplGetParent(); } // In order to limit drawing, an update only draws the window which // has PAINTALLCHILDREN set pWindow = pUpdateWindow; do { if ( pWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren ) pUpdateWindow = pWindow; if ( pWindow->ImplIsOverlapWindow() ) break; pWindow = pWindow->ImplGetParent(); } while ( pWindow ); // if there is something to paint, trigger a Paint if ( pUpdateWindow->mpWindowImpl->mnPaintFlags & (ImplPaintFlags::Paint | ImplPaintFlags::PaintChildren) ) { VclPtr xWindow(this); // trigger an update also for system windows on top of us, // otherwise holes would remain vcl::Window* pUpdateOverlapWindow = ImplGetFirstOverlapWindow(); if (pUpdateOverlapWindow->mpWindowImpl) pUpdateOverlapWindow = pUpdateOverlapWindow->mpWindowImpl->mpFirstOverlap; else pUpdateOverlapWindow = nullptr; while ( pUpdateOverlapWindow ) { pUpdateOverlapWindow->PaintImmediately(); pUpdateOverlapWindow = pUpdateOverlapWindow->mpWindowImpl->mpNext; } pUpdateWindow->ImplCallPaint(nullptr, pUpdateWindow->mpWindowImpl->mnPaintFlags); if (comphelper::LibreOfficeKit::isActive() && pUpdateWindow->GetParentDialog()) pUpdateWindow->LogicInvalidate(nullptr); if (xWindow->isDisposed()) return; bFlush = true; } if ( bFlush ) GetOutDev()->Flush(); } void Window::ImplPaintToDevice( OutputDevice* i_pTargetOutDev, const Point& i_rPos ) { // Special drawing when called through LOKit // TODO: Move to its own method if (comphelper::LibreOfficeKit::isActive()) { VclPtrInstance pDevice(*i_pTargetOutDev); pDevice->EnableRTL(IsRTLEnabled()); Size aSize(GetOutputSizePixel()); pDevice->SetOutputSizePixel(aSize); vcl::Font aCopyFont = GetFont(); pDevice->SetFont(aCopyFont); pDevice->SetTextColor(GetTextColor()); if (GetOutDev()->IsLineColor()) pDevice->SetLineColor(GetOutDev()->GetLineColor()); else pDevice->SetLineColor(); if (GetOutDev()->IsFillColor()) pDevice->SetFillColor(GetOutDev()->GetFillColor()); else pDevice->SetFillColor(); if (IsTextLineColor()) pDevice->SetTextLineColor(GetTextLineColor()); else pDevice->SetTextLineColor(); if (IsOverlineColor()) pDevice->SetOverlineColor(GetOverlineColor()); else pDevice->SetOverlineColor(); if (IsTextFillColor()) pDevice->SetTextFillColor(GetTextFillColor()); else pDevice->SetTextFillColor(); pDevice->SetTextAlign(GetTextAlign()); pDevice->SetRasterOp(GetOutDev()->GetRasterOp()); tools::Rectangle aPaintRect(Point(), GetOutputSizePixel()); vcl::Region aClipRegion(GetOutDev()->GetClipRegion()); pDevice->SetClipRegion(); aClipRegion.Intersect(aPaintRect); pDevice->SetClipRegion(aClipRegion); if (!IsPaintTransparent() && IsBackground() && ! (GetParentClipMode() & ParentClipMode::NoClip)) Erase(*pDevice); pDevice->SetMapMode(GetMapMode()); Paint(*pDevice, tools::Rectangle(Point(), GetOutputSizePixel())); i_pTargetOutDev->DrawOutDev(i_rPos, aSize, Point(), pDevice->PixelToLogic(aSize), *pDevice); bool bHasMirroredGraphics = pDevice->HasMirroredGraphics(); // get rid of virtual device now so they don't pile up during recursive calls pDevice.disposeAndClear(); for( vcl::Window* pChild = mpWindowImpl->mpFirstChild; pChild; pChild = pChild->mpWindowImpl->mpNext ) { if( pChild->mpWindowImpl->mpFrame == mpWindowImpl->mpFrame && pChild->IsVisible() ) { tools::Long nDeltaX = pChild->GetOutDev()->mnOutOffX - GetOutDev()->mnOutOffX; if( bHasMirroredGraphics ) nDeltaX = GetOutDev()->mnOutWidth - nDeltaX - pChild->GetOutDev()->mnOutWidth; tools::Long nDeltaY = pChild->GetOutOffYPixel() - GetOutOffYPixel(); Point aPos( i_rPos ); aPos += Point(nDeltaX, nDeltaY); pChild->ImplPaintToDevice( i_pTargetOutDev, aPos ); } } return; } bool bRVisible = mpWindowImpl->mbReallyVisible; mpWindowImpl->mbReallyVisible = mpWindowImpl->mbVisible; bool bDevOutput = GetOutDev()->mbDevOutput; GetOutDev()->mbDevOutput = true; const OutputDevice *pOutDev = GetOutDev(); tools::Long nOldDPIX = pOutDev->GetDPIX(); tools::Long nOldDPIY = pOutDev->GetDPIY(); GetOutDev()->mnDPIX = i_pTargetOutDev->GetDPIX(); GetOutDev()->mnDPIY = i_pTargetOutDev->GetDPIY(); bool bOutput = GetOutDev()->IsOutputEnabled(); GetOutDev()->EnableOutput(); SAL_WARN_IF( GetMapMode().GetMapUnit() != MapUnit::MapPixel, "vcl.window", "MapMode must be PIXEL based" ); if ( GetMapMode().GetMapUnit() != MapUnit::MapPixel ) return; // preserve graphicsstate GetOutDev()->Push(); vcl::Region aClipRegion( GetOutDev()->GetClipRegion() ); GetOutDev()->SetClipRegion(); GDIMetaFile* pOldMtf = GetOutDev()->GetConnectMetaFile(); GDIMetaFile aMtf; GetOutDev()->SetConnectMetaFile( &aMtf ); // put a push action to metafile GetOutDev()->Push(); // copy graphics state to metafile vcl::Font aCopyFont = GetFont(); if( nOldDPIX != GetOutDev()->mnDPIX || nOldDPIY != GetOutDev()->mnDPIY ) { aCopyFont.SetFontHeight( aCopyFont.GetFontHeight() * GetOutDev()->mnDPIY / nOldDPIY ); aCopyFont.SetAverageFontWidth( aCopyFont.GetAverageFontWidth() * GetOutDev()->mnDPIX / nOldDPIX ); } SetFont( aCopyFont ); SetTextColor( GetTextColor() ); if( GetOutDev()->IsLineColor() ) GetOutDev()->SetLineColor( GetOutDev()->GetLineColor() ); else GetOutDev()->SetLineColor(); if( GetOutDev()->IsFillColor() ) GetOutDev()->SetFillColor( GetOutDev()->GetFillColor() ); else GetOutDev()->SetFillColor(); if( IsTextLineColor() ) SetTextLineColor( GetTextLineColor() ); else SetTextLineColor(); if( IsOverlineColor() ) SetOverlineColor( GetOverlineColor() ); else SetOverlineColor(); if( IsTextFillColor() ) SetTextFillColor( GetTextFillColor() ); else SetTextFillColor(); SetTextAlign( GetTextAlign() ); GetOutDev()->SetRasterOp( GetOutDev()->GetRasterOp() ); if( GetOutDev()->IsRefPoint() ) GetOutDev()->SetRefPoint( GetOutDev()->GetRefPoint() ); else GetOutDev()->SetRefPoint(); GetOutDev()->SetLayoutMode( GetOutDev()->GetLayoutMode() ); GetOutDev()->SetDigitLanguage( GetOutDev()->GetDigitLanguage() ); tools::Rectangle aPaintRect(Point(0, 0), GetOutputSizePixel()); aClipRegion.Intersect( aPaintRect ); GetOutDev()->SetClipRegion( aClipRegion ); // do the actual paint // background if( ! IsPaintTransparent() && IsBackground() && ! (GetParentClipMode() & ParentClipMode::NoClip ) ) { Erase(*GetOutDev()); } // foreground Paint(*GetOutDev(), aPaintRect); // put a pop action to metafile GetOutDev()->Pop(); GetOutDev()->SetConnectMetaFile( pOldMtf ); GetOutDev()->EnableOutput( bOutput ); mpWindowImpl->mbReallyVisible = bRVisible; // paint metafile to VDev VclPtrInstance pMaskedDevice(*i_pTargetOutDev, DeviceFormat::WITH_ALPHA); pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel(), true, true ); pMaskedDevice->EnableRTL( IsRTLEnabled() ); aMtf.WindStart(); aMtf.Play(*pMaskedDevice); BitmapEx aBmpEx( pMaskedDevice->GetBitmapEx( Point( 0, 0 ), aPaintRect.GetSize() ) ); i_pTargetOutDev->DrawBitmapEx( i_rPos, aBmpEx ); // get rid of virtual device now so they don't pile up during recursive calls pMaskedDevice.disposeAndClear(); for( vcl::Window* pChild = mpWindowImpl->mpFirstChild; pChild; pChild = pChild->mpWindowImpl->mpNext ) { if( pChild->mpWindowImpl->mpFrame == mpWindowImpl->mpFrame && pChild->IsVisible() ) { tools::Long nDeltaX = pChild->GetOutDev()->mnOutOffX - GetOutDev()->mnOutOffX; if( pOutDev->HasMirroredGraphics() ) nDeltaX = GetOutDev()->mnOutWidth - nDeltaX - pChild->GetOutDev()->mnOutWidth; tools::Long nDeltaY = pChild->GetOutOffYPixel() - GetOutOffYPixel(); Point aPos( i_rPos ); Point aDelta( nDeltaX, nDeltaY ); aPos += aDelta; pChild->ImplPaintToDevice( i_pTargetOutDev, aPos ); } } // restore graphics state GetOutDev()->Pop(); GetOutDev()->EnableOutput( bOutput ); mpWindowImpl->mbReallyVisible = bRVisible; GetOutDev()->mbDevOutput = bDevOutput; GetOutDev()->mnDPIX = nOldDPIX; GetOutDev()->mnDPIY = nOldDPIY; } void Window::PaintToDevice(OutputDevice* pDev, const Point& rPos) { if( !mpWindowImpl ) return; SAL_WARN_IF( pDev->HasMirroredGraphics(), "vcl.window", "PaintToDevice to mirroring graphics" ); SAL_WARN_IF( pDev->IsRTLEnabled(), "vcl.window", "PaintToDevice to mirroring device" ); vcl::Window* pRealParent = nullptr; if( ! mpWindowImpl->mbVisible ) { vcl::Window* pTempParent = ImplGetDefaultWindow(); pTempParent->EnableChildTransparentMode(); pRealParent = GetParent(); SetParent( pTempParent ); // trigger correct visibility flags for children Show(); Hide(); } bool bVisible = mpWindowImpl->mbVisible; mpWindowImpl->mbVisible = true; if( mpWindowImpl->mpBorderWindow ) mpWindowImpl->mpBorderWindow->ImplPaintToDevice( pDev, rPos ); else ImplPaintToDevice( pDev, rPos ); mpWindowImpl->mbVisible = bVisible; if( pRealParent ) SetParent( pRealParent ); } void Window::Erase(vcl::RenderContext& rRenderContext) { if (!GetOutDev()->IsDeviceOutputNecessary() || GetOutDev()->ImplIsRecordLayout()) return; bool bNativeOK = false; ControlPart aCtrlPart = ImplGetWindowImpl()->mnNativeBackground; if (aCtrlPart == ControlPart::Entire && IsControlBackground()) { // nothing to do here; background is drawn in corresponding drawNativeControl implementation bNativeOK = true; } else if (aCtrlPart != ControlPart::NONE && ! IsControlBackground()) { tools::Rectangle aCtrlRegion(Point(), GetOutputSizePixel()); ControlState nState = ControlState::NONE; if (IsEnabled()) nState |= ControlState::ENABLED; bNativeOK = rRenderContext.DrawNativeControl(ControlType::WindowBackground, aCtrlPart, aCtrlRegion, nState, ImplControlValue(), OUString()); } if (GetOutDev()->mbBackground && !bNativeOK) { RasterOp eRasterOp = GetOutDev()->GetRasterOp(); if (eRasterOp != RasterOp::OverPaint) GetOutDev()->SetRasterOp(RasterOp::OverPaint); rRenderContext.DrawWallpaper(0, 0, GetOutDev()->mnOutWidth, GetOutDev()->mnOutHeight, GetOutDev()->maBackground); if (eRasterOp != RasterOp::OverPaint) rRenderContext.SetRasterOp(eRasterOp); } if (GetOutDev()->mpAlphaVDev) GetOutDev()->mpAlphaVDev->Erase(); } void Window::ImplScroll( const tools::Rectangle& rRect, tools::Long nHorzScroll, tools::Long nVertScroll, ScrollFlags nFlags ) { if ( !GetOutDev()->IsDeviceOutputNecessary() ) return; nHorzScroll = GetOutDev()->ImplLogicWidthToDevicePixel( nHorzScroll ); nVertScroll = GetOutDev()->ImplLogicHeightToDevicePixel( nVertScroll ); if ( !nHorzScroll && !nVertScroll ) return; // There will be no CopyArea() call below, so invalidate the whole visible // area, not only the smaller one that was just scrolled in. // Do this when we have a double buffer anyway, or (tdf#152094) the device has a map mode enabled which // makes the conversion to pixel inaccurate const bool bCopyExistingAreaAndElideInvalidate = !SupportsDoubleBuffering() && !GetOutDev()->IsMapModeEnabled(); if ( mpWindowImpl->mpCursor ) mpWindowImpl->mpCursor->ImplSuspend(); ScrollFlags nOrgFlags = nFlags; if ( !(nFlags & (ScrollFlags::Children | ScrollFlags::NoChildren)) ) { if ( GetStyle() & WB_CLIPCHILDREN ) nFlags |= ScrollFlags::NoChildren; else nFlags |= ScrollFlags::Children; } vcl::Region aInvalidateRegion; bool bScrollChildren(nFlags & ScrollFlags::Children); if ( !mpWindowImpl->mpFirstChild ) bScrollChildren = false; OutputDevice *pOutDev = GetOutDev(); // RTL: check if this window requires special action bool bReMirror = GetOutDev()->ImplIsAntiparallel(); tools::Rectangle aRectMirror( rRect ); if( bReMirror ) { // make sure the invalidate region of this window is // computed in the same coordinate space as the one from the overlap windows pOutDev->ReMirror( aRectMirror ); } // adapt paint areas ImplMoveAllInvalidateRegions( aRectMirror, nHorzScroll, nVertScroll, bScrollChildren ); ImplCalcOverlapRegion( aRectMirror, aInvalidateRegion, !bScrollChildren, false ); // if the scrolling on the device is performed in the opposite direction // then move the overlaps in that direction to compute the invalidate region // on the correct side, i.e., revert nHorzScroll if (!aInvalidateRegion.IsEmpty()) { aInvalidateRegion.Move(bReMirror ? -nHorzScroll : nHorzScroll, nVertScroll); } tools::Rectangle aDestRect(aRectMirror); aDestRect.Move(bReMirror ? -nHorzScroll : nHorzScroll, nVertScroll); vcl::Region aWinInvalidateRegion(aRectMirror); if (bCopyExistingAreaAndElideInvalidate) aWinInvalidateRegion.Exclude(aDestRect); aInvalidateRegion.Union(aWinInvalidateRegion); vcl::Region aRegion( GetOutputRectPixel() ); if ( nFlags & ScrollFlags::Clip ) aRegion.Intersect( rRect ); if ( mpWindowImpl->mbWinRegion ) aRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) ); aRegion.Exclude( aInvalidateRegion ); ImplClipBoundaries( aRegion, false, true ); if ( !bScrollChildren ) { if ( nOrgFlags & ScrollFlags::NoChildren ) ImplClipAllChildren( aRegion ); else ImplClipChildren( aRegion ); } if ( GetOutDev()->mbClipRegion && (nFlags & ScrollFlags::UseClipRegion) ) aRegion.Intersect( GetOutDev()->maRegion ); if ( !aRegion.IsEmpty() ) { if ( mpWindowImpl->mpWinData ) { if ( mpWindowImpl->mbFocusVisible ) ImplInvertFocus( *mpWindowImpl->mpWinData->mpFocusRect ); if ( mpWindowImpl->mbTrackVisible && (mpWindowImpl->mpWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) ) InvertTracking( *mpWindowImpl->mpWinData->mpTrackRect, mpWindowImpl->mpWinData->mnTrackFlags ); } #ifndef IOS // This seems completely unnecessary with tiled rendering, and // causes the "AquaSalGraphics::copyArea() for non-layered // graphics" message. Presumably we should bypass this on all // platforms when dealing with a "window" that uses tiled // rendering at the moment. Unclear how to figure that out, // though. Also unclear whether we actually could just not // create a "frame window", whatever that exactly is, in the // tiled rendering case, or at least for platforms where tiles // rendering is all there is. SalGraphics* pGraphics = ImplGetFrameGraphics(); // The invalidation area contains the area what would be copied here, // so avoid copying in case of double buffering. if (pGraphics && bCopyExistingAreaAndElideInvalidate) { if( bReMirror ) { pOutDev->ReMirror( aRegion ); } pOutDev->SelectClipRegion( aRegion, pGraphics ); pGraphics->CopyArea( rRect.Left()+nHorzScroll, rRect.Top()+nVertScroll, rRect.Left(), rRect.Top(), rRect.GetWidth(), rRect.GetHeight(), *GetOutDev() ); } #endif if ( mpWindowImpl->mpWinData ) { if ( mpWindowImpl->mbFocusVisible ) ImplInvertFocus( *mpWindowImpl->mpWinData->mpFocusRect ); if ( mpWindowImpl->mbTrackVisible && (mpWindowImpl->mpWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) ) InvertTracking( *mpWindowImpl->mpWinData->mpTrackRect, mpWindowImpl->mpWinData->mnTrackFlags ); } } if ( !aInvalidateRegion.IsEmpty() ) { // RTL: the invalidate region for this windows is already computed in frame coordinates // so it has to be re-mirrored before calling the Paint-handler mpWindowImpl->mnPaintFlags |= ImplPaintFlags::CheckRtl; if ( !bScrollChildren ) { if ( nOrgFlags & ScrollFlags::NoChildren ) ImplClipAllChildren( aInvalidateRegion ); else ImplClipChildren( aInvalidateRegion ); } ImplInvalidateFrameRegion( &aInvalidateRegion, InvalidateFlags::Children ); } if ( bScrollChildren ) { vcl::Window* pWindow = mpWindowImpl->mpFirstChild; while ( pWindow ) { Point aPos = pWindow->GetPosPixel(); aPos += Point( nHorzScroll, nVertScroll ); pWindow->SetPosPixel( aPos ); pWindow = pWindow->mpWindowImpl->mpNext; } } if ( nFlags & ScrollFlags::Update ) PaintImmediately(); if ( mpWindowImpl->mpCursor ) mpWindowImpl->mpCursor->ImplResume(); } } /* namespace vcl */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */