Files
libreoffice/vcl/source/control/ctrl.cxx
Pranav Kant 446a37ece3 lokdialog: Support painting parts of the dialog
Pass the dimensions of the region to the paintDialog call to paint only
that much of the region in the dialog.

The DIALOG_INVALIDATE callback also returns a 'rectangle' field now in
the payload that tells the region of the dialog invalidated. It can be
used in combination with the new paintDialog call then to paint only the
invalidated region in the dialog.

Change-Id: Iebb228865c71684e0f75dd01271b71ae41a0f906
Reviewed-on: https://gerrit.libreoffice.org/44472
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: pranavk <pranavk@collabora.co.uk>
2017-11-09 04:12:50 +01:00

511 lines
16 KiB
C++

/* -*- 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 <comphelper/processfactory.hxx>
#include <comphelper/lok.hxx>
#include <vcl/svapp.hxx>
#include <vcl/event.hxx>
#include <vcl/ctrl.hxx>
#include <vcl/floatwin.hxx>
#include <vcl/decoview.hxx>
#include <vcl/dialog.hxx>
#include <vcl/salnativewidgets.hxx>
#include <vcl/settings.hxx>
#include <vcl/uitest/logger.hxx>
#include <textlayout.hxx>
#include <svdata.hxx>
#include <controldata.hxx>
using namespace vcl;
void Control::ImplInitControlData()
{
mbHasControlFocus = false;
mbShowAccelerator = false;
mpControlData = new ImplControlData;
}
Control::Control( WindowType nType ) :
Window( nType )
{
ImplInitControlData();
}
Control::Control( vcl::Window* pParent, WinBits nStyle ) :
Window( WindowType::CONTROL )
{
ImplInitControlData();
ImplInit( pParent, nStyle, nullptr );
}
Control::~Control()
{
disposeOnce();
}
void Control::dispose()
{
delete mpControlData;
mpControlData = nullptr;
Window::dispose();
}
void Control::EnableRTL( bool bEnable )
{
// convenience: for controls also switch layout mode
SetLayoutMode( bEnable ? ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft :
ComplexTextLayoutFlags::TextOriginLeft );
CompatStateChanged( StateChangedType::Mirroring );
OutputDevice::EnableRTL(bEnable);
}
void Control::Resize()
{
ImplClearLayoutData();
Window::Resize();
}
void Control::FillLayoutData() const
{
}
void Control::CreateLayoutData() const
{
SAL_WARN_IF( mpControlData->mpLayoutData, "vcl", "Control::CreateLayoutData: should be called with non-existent layout data only!" );
mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
}
bool Control::HasLayoutData() const
{
return mpControlData && mpControlData->mpLayoutData != nullptr;
}
void Control::SetText( const OUString& rStr )
{
ImplClearLayoutData();
Window::SetText( rStr );
}
ControlLayoutData::ControlLayoutData() : m_pParent( nullptr )
{
}
tools::Rectangle ControlLayoutData::GetCharacterBounds( long nIndex ) const
{
return (nIndex >= 0 && nIndex < (long) m_aUnicodeBoundRects.size()) ? m_aUnicodeBoundRects[ nIndex ] : tools::Rectangle();
}
tools::Rectangle Control::GetCharacterBounds( long nIndex ) const
{
if( !HasLayoutData() )
FillLayoutData();
return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->GetCharacterBounds( nIndex ) : tools::Rectangle();
}
long ControlLayoutData::GetIndexForPoint( const Point& rPoint ) const
{
long nIndex = -1;
for( long i = m_aUnicodeBoundRects.size()-1; i >= 0; i-- )
{
Point aTopLeft = m_aUnicodeBoundRects[i].TopLeft();
Point aBottomRight = m_aUnicodeBoundRects[i].BottomRight();
if (rPoint.X() >= aTopLeft.X() && rPoint.Y() >= aTopLeft.Y() &&
rPoint.X() <= aBottomRight.X() && rPoint.Y() <= aBottomRight.Y())
{
nIndex = i;
break;
}
}
return nIndex;
}
long Control::GetIndexForPoint( const Point& rPoint ) const
{
if( ! HasLayoutData() )
FillLayoutData();
return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->GetIndexForPoint( rPoint ) : -1;
}
long ControlLayoutData::GetLineCount() const
{
long nLines = m_aLineIndices.size();
if( nLines == 0 && !m_aDisplayText.isEmpty() )
nLines = 1;
return nLines;
}
Pair ControlLayoutData::GetLineStartEnd( long nLine ) const
{
Pair aPair( -1, -1 );
int nDisplayLines = m_aLineIndices.size();
if( nLine >= 0 && nLine < nDisplayLines )
{
aPair.A() = m_aLineIndices[nLine];
if( nLine+1 < nDisplayLines )
aPair.B() = m_aLineIndices[nLine+1]-1;
else
aPair.B() = m_aDisplayText.getLength()-1;
}
else if( nLine == 0 && nDisplayLines == 0 && !m_aDisplayText.isEmpty() )
{
// special case for single line controls so the implementations
// in that case do not have to fill in the line indices
aPair.A() = 0;
aPair.B() = m_aDisplayText.getLength()-1;
}
return aPair;
}
Pair Control::GetLineStartEnd( long nLine ) const
{
if( !HasLayoutData() )
FillLayoutData();
return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->GetLineStartEnd( nLine ) : Pair( -1, -1 );
}
long ControlLayoutData::ToRelativeLineIndex( long nIndex ) const
{
// is the index sensible at all ?
if( nIndex >= 0 && nIndex < m_aDisplayText.getLength() )
{
int nDisplayLines = m_aLineIndices.size();
// if only 1 line exists, then absolute and relative index are
// identical -> do nothing
if( nDisplayLines > 1 )
{
int nLine;
for( nLine = nDisplayLines-1; nLine >= 0; nLine-- )
{
if( m_aLineIndices[nLine] <= nIndex )
{
nIndex -= m_aLineIndices[nLine];
break;
}
}
if( nLine < 0 )
{
SAL_WARN_IF( nLine < 0, "vcl", "ToRelativeLineIndex failed" );
nIndex = -1;
}
}
}
else
nIndex = -1;
return nIndex;
}
long Control::ToRelativeLineIndex( long nIndex ) const
{
if( !HasLayoutData() )
FillLayoutData();
return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->ToRelativeLineIndex( nIndex ) : -1;
}
OUString Control::GetDisplayText() const
{
if( !HasLayoutData() )
FillLayoutData();
return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->m_aDisplayText : GetText();
}
bool Control::EventNotify( NotifyEvent& rNEvt )
{
// tdf#91081 if control is not valid, skip the emission - chaining to the parent
if (mpControlData)
{
if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
{
if ( !mbHasControlFocus )
{
mbHasControlFocus = true;
CompatStateChanged( StateChangedType::ControlFocus );
if ( ImplCallEventListenersAndHandler( VclEventId::ControlGetFocus, [this] () { maGetFocusHdl.Call(*this); } ) )
// been destroyed within the handler
return true;
}
}
else
{
if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
{
vcl::Window* pFocusWin = Application::GetFocusWindow();
if ( !pFocusWin || !ImplIsWindowOrChild( pFocusWin ) )
{
mbHasControlFocus = false;
CompatStateChanged( StateChangedType::ControlFocus );
if ( ImplCallEventListenersAndHandler( VclEventId::ControlLoseFocus, [this] () { maLoseFocusHdl.Call(*this); } ) )
// been destroyed within the handler
return true;
}
}
}
}
return Window::EventNotify( rNEvt );
}
void Control::StateChanged( StateChangedType nStateChange )
{
if( nStateChange == StateChangedType::InitShow ||
nStateChange == StateChangedType::Visible ||
nStateChange == StateChangedType::Zoom ||
nStateChange == StateChangedType::ControlFont
)
{
ImplClearLayoutData();
}
Window::StateChanged( nStateChange );
}
void Control::AppendLayoutData( const Control& rSubControl ) const
{
if( !rSubControl.HasLayoutData() )
rSubControl.FillLayoutData();
if( !rSubControl.HasLayoutData() || rSubControl.mpControlData->mpLayoutData->m_aDisplayText.isEmpty() )
return;
long nCurrentIndex = mpControlData->mpLayoutData->m_aDisplayText.getLength();
mpControlData->mpLayoutData->m_aDisplayText += rSubControl.mpControlData->mpLayoutData->m_aDisplayText;
int nLines = rSubControl.mpControlData->mpLayoutData->m_aLineIndices.size();
int n;
mpControlData->mpLayoutData->m_aLineIndices.push_back( nCurrentIndex );
for( n = 1; n < nLines; n++ )
mpControlData->mpLayoutData->m_aLineIndices.push_back( rSubControl.mpControlData->mpLayoutData->m_aLineIndices[n] + nCurrentIndex );
int nRectangles = rSubControl.mpControlData->mpLayoutData->m_aUnicodeBoundRects.size();
tools::Rectangle aRel = const_cast<Control&>(rSubControl).GetWindowExtentsRelative( const_cast<Control*>(this) );
for( n = 0; n < nRectangles; n++ )
{
tools::Rectangle aRect = rSubControl.mpControlData->mpLayoutData->m_aUnicodeBoundRects[n];
aRect.Move( aRel.Left(), aRel.Top() );
mpControlData->mpLayoutData->m_aUnicodeBoundRects.push_back( aRect );
}
}
bool Control::ImplCallEventListenersAndHandler( VclEventId nEvent, std::function<void()> const & callHandler )
{
VclPtr<Control> xThis(this);
UITestLogger::getInstance().logAction(xThis, nEvent);
CallEventListeners( nEvent );
if ( !xThis->IsDisposed() )
{
if (callHandler)
{
callHandler();
}
if ( !xThis->IsDisposed() )
return false;
}
return true;
}
void Control::SetLayoutDataParent( const Control* pParent ) const
{
if( HasLayoutData() )
mpControlData->mpLayoutData->m_pParent = pParent;
}
void Control::ImplClearLayoutData() const
{
mpControlData->mpLayoutData.reset();
}
void Control::ImplDrawFrame( OutputDevice* pDev, tools::Rectangle& rRect )
{
// use a deco view to draw the frame
// However, since there happens a lot of magic there, we need to fake some (style) settings
// on the device
AllSettings aOriginalSettings( pDev->GetSettings() );
AllSettings aNewSettings( aOriginalSettings );
StyleSettings aStyle( aNewSettings.GetStyleSettings() );
// The *only known* clients of the Draw methods of the various VCL-controls are form controls:
// During print preview, and during printing, Draw is called. Thus, drawing always happens with a
// mono (colored) border
aStyle.SetOptions( aStyle.GetOptions() | StyleSettingsOptions::Mono );
aStyle.SetMonoColor( GetSettings().GetStyleSettings().GetMonoColor() );
aNewSettings.SetStyleSettings( aStyle );
// #i67023# do not call data changed listeners for this fake
// since they may understandably invalidate on settings changed
pDev->OutputDevice::SetSettings( aNewSettings );
DecorationView aDecoView( pDev );
rRect = aDecoView.DrawFrame( rRect, DrawFrameStyle::Out, DrawFrameFlags::WindowBorder );
pDev->OutputDevice::SetSettings( aOriginalSettings );
}
void Control::SetShowAccelerator(bool bVal)
{
mbShowAccelerator = bVal;
};
ControlLayoutData::~ControlLayoutData()
{
if( m_pParent )
m_pParent->ImplClearLayoutData();
}
Size Control::GetOptimalSize() const
{
return Size( GetTextWidth( GetText() ) + 2 * 12,
GetTextHeight() + 2 * 6 );
}
void Control::SetReferenceDevice( OutputDevice* _referenceDevice )
{
if ( mpControlData->mpReferenceDevice == _referenceDevice )
return;
mpControlData->mpReferenceDevice = _referenceDevice;
Invalidate();
}
OutputDevice* Control::GetReferenceDevice() const
{
return mpControlData->mpReferenceDevice;
}
const vcl::Font& Control::GetCanonicalFont( const StyleSettings& _rStyle ) const
{
return _rStyle.GetLabelFont();
}
const Color& Control::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
{
return _rStyle.GetLabelTextColor();
}
void Control::ApplySettings(vcl::RenderContext& rRenderContext)
{
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
vcl::Font rFont(GetCanonicalFont(rStyleSettings));
ApplyControlFont(rRenderContext, rFont);
ApplyControlForeground(rRenderContext, GetCanonicalTextColor(rStyleSettings));
rRenderContext.SetTextFillColor();
}
void Control::ImplInitSettings()
{
ApplySettings(*this);
}
void Control::LogicInvalidate(const tools::Rectangle* /*pRectangle*/)
{
// Several repaint, resize invalidations are emitted when we are painting,
// ignore all of those
if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isDialogPainting())
{
// If parent is a floating window, trigger an invalidate there
vcl::Window* pWindow = this;
while (pWindow)
{
if (pWindow->ImplIsFloatingWindow())
{
static_cast<FloatingWindow*>(pWindow)->LogicInvalidate(nullptr);
return;
}
pWindow = pWindow->GetParent();
}
// otherwise, for now, just invalidate the whole dialog
Dialog* pParentDlg = GetParentDialog();
const tools::Rectangle aRect(Point(GetOutOffXPixel(), GetOutOffYPixel()), Size(GetOutputWidthPixel(), GetOutputHeightPixel()));
if (pParentDlg)
pParentDlg->LogicInvalidate(&aRect);
}
}
tools::Rectangle Control::DrawControlText( OutputDevice& _rTargetDevice, const tools::Rectangle& rRect, const OUString& _rStr,
DrawTextFlags _nStyle, MetricVector* _pVector, OUString* _pDisplayText, const Size* i_pDeviceSize ) const
{
OUString rPStr = _rStr;
DrawTextFlags nPStyle = _nStyle;
bool accel = ImplGetSVData()->maNWFData.mbEnableAccel;
bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
if (!accel || (autoacc && !mbShowAccelerator))
{
rPStr = GetNonMnemonicString( _rStr );
nPStyle &= ~DrawTextFlags::HideMnemonic;
}
if ( !mpControlData->mpReferenceDevice || ( mpControlData->mpReferenceDevice == &_rTargetDevice ) )
{
const tools::Rectangle aRet = _rTargetDevice.GetTextRect(rRect, rPStr, nPStyle);
_rTargetDevice.DrawText(aRet, rPStr, nPStyle, _pVector, _pDisplayText);
return aRet;
}
ControlTextRenderer aRenderer( *this, _rTargetDevice, *mpControlData->mpReferenceDevice );
return aRenderer.DrawText(rRect, rPStr, nPStyle, _pVector, _pDisplayText, i_pDeviceSize);
}
tools::Rectangle Control::GetControlTextRect( OutputDevice& _rTargetDevice, const tools::Rectangle & rRect,
const OUString& _rStr, DrawTextFlags _nStyle, Size* o_pDeviceSize ) const
{
OUString rPStr = _rStr;
DrawTextFlags nPStyle = _nStyle;
bool accel = ImplGetSVData()->maNWFData.mbEnableAccel;
bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
if (!accel || (autoacc && !mbShowAccelerator))
{
rPStr = GetNonMnemonicString( _rStr );
nPStyle &= ~DrawTextFlags::HideMnemonic;
}
if ( !mpControlData->mpReferenceDevice || ( mpControlData->mpReferenceDevice == &_rTargetDevice ) )
{
tools::Rectangle aRet = _rTargetDevice.GetTextRect( rRect, rPStr, nPStyle );
if (o_pDeviceSize)
{
*o_pDeviceSize = aRet.GetSize();
}
return aRet;
}
ControlTextRenderer aRenderer( *this, _rTargetDevice, *mpControlData->mpReferenceDevice );
return aRenderer.GetTextRect(rRect, rPStr, nPStyle, o_pDeviceSize);
}
Font
Control::GetUnzoomedControlPointFont() const
{
Font aFont(GetCanonicalFont(GetSettings().GetStyleSettings()));
if (IsControlFont())
aFont.Merge(GetControlFont());
return aFont;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */