diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/source/window/paint.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/4%7.4.7.tar.xz libreoffice-upstream/4%7.4.7.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/window/paint.cxx')
-rw-r--r-- | vcl/source/window/paint.cxx | 1809 |
1 files changed, 1809 insertions, 0 deletions
diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx new file mode 100644 index 000000000..a70f1c0e4 --- /dev/null +++ b/vcl/source/window/paint.cxx @@ -0,0 +1,1809 @@ +/* -*- 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 <config_features.h> +#include <vcl/gdimtf.hxx> +#include <vcl/window.hxx> +#include <vcl/virdev.hxx> +#include <vcl/cursor.hxx> +#include <vcl/settings.hxx> +#include <vcl/syswin.hxx> + +#include <sal/types.h> +#include <sal/log.hxx> + +#include <window.h> +#include <salgdi.hxx> +#include <salframe.hxx> +#include <svdata.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/profilezone.hxx> +#if HAVE_FEATURE_OPENGL +#include <vcl/opengl/OpenGLHelper.hxx> +#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<vcl::Window> m_pWindow; + std::unique_ptr<vcl::Region> 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<const SystemWindow*>(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<tools::Rectangle *>(&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); +} + +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<vcl::LOKPayloadItem> 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(), "invalidate", aPayload); + } + // Added for dialog items. Pass invalidation to the parent window. + else if (VclPtr<vcl::Window> 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<vcl::Window> 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<VirtualDevice> 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<VirtualDevice> pMaskedDevice(*i_pTargetOutDev, + DeviceFormat::DEFAULT, + DeviceFormat::DEFAULT); + + pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel() ); + 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: */ |