Update git submodules
[LibreOffice.git] / vcl / source / control / ctrl.cxx
blob4f7a42badffc0a3beac2ff2cfaca5c25382f88a8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/lok.hxx>
21 #include <o3tl/safeint.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/event.hxx>
24 #include <vcl/ctrl.hxx>
25 #include <vcl/decoview.hxx>
26 #include <vcl/mnemonic.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/uitest/logger.hxx>
29 #include <vcl/DocWindow.hxx>
30 #include <sal/log.hxx>
32 #include <textlayout.hxx>
33 #include <svdata.hxx>
35 using namespace vcl;
37 void Control::ImplInitControlData()
39 mbHasControlFocus = false;
40 mbShowAccelerator = false;
43 Control::Control( WindowType nType ) :
44 Window( nType )
46 ImplInitControlData();
49 Control::Control( vcl::Window* pParent, WinBits nStyle ) :
50 Window( WindowType::CONTROL )
52 ImplInitControlData();
53 ImplInit( pParent, nStyle, nullptr );
56 Control::~Control()
58 disposeOnce();
61 void Control::dispose()
63 mxLayoutData.reset();
64 mpReferenceDevice.clear();
65 Window::dispose();
68 void Control::EnableRTL( bool bEnable )
70 // convenience: for controls also switch layout mode
71 GetOutDev()->SetLayoutMode( bEnable ? vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft :
72 vcl::text::ComplexTextLayoutFlags::TextOriginLeft );
73 CompatStateChanged( StateChangedType::Mirroring );
74 Window::EnableRTL(bEnable);
77 void Control::Resize()
79 ImplClearLayoutData();
80 Window::Resize();
83 void Control::FillLayoutData() const
87 void Control::CreateLayoutData() const
89 SAL_WARN_IF( mxLayoutData, "vcl", "Control::CreateLayoutData: should be called with non-existent layout data only!" );
90 mxLayoutData.emplace();
93 bool Control::HasLayoutData() const
95 return bool(mxLayoutData);
98 void Control::SetText( const OUString& rStr )
100 ImplClearLayoutData();
101 Window::SetText( rStr );
104 ControlLayoutData::ControlLayoutData() : m_pParent( nullptr )
108 tools::Rectangle ControlLayoutData::GetCharacterBounds( tools::Long nIndex ) const
110 return (nIndex >= 0 && o3tl::make_unsigned(nIndex) < m_aUnicodeBoundRects.size()) ? m_aUnicodeBoundRects[ nIndex ] : tools::Rectangle();
113 tools::Rectangle Control::GetCharacterBounds( tools::Long nIndex ) const
115 if( !HasLayoutData() )
116 FillLayoutData();
117 return mxLayoutData ? mxLayoutData->GetCharacterBounds( nIndex ) : tools::Rectangle();
120 tools::Long ControlLayoutData::GetIndexForPoint( const Point& rPoint ) const
122 tools::Long nIndex = -1;
123 for( tools::Long i = m_aUnicodeBoundRects.size()-1; i >= 0; i-- )
125 Point aTopLeft = m_aUnicodeBoundRects[i].TopLeft();
126 Point aBottomRight = m_aUnicodeBoundRects[i].BottomRight();
127 if (rPoint.X() >= aTopLeft.X() && rPoint.Y() >= aTopLeft.Y() &&
128 rPoint.X() <= aBottomRight.X() && rPoint.Y() <= aBottomRight.Y())
130 nIndex = i;
131 break;
134 return nIndex;
137 tools::Long Control::GetIndexForPoint( const Point& rPoint ) const
139 if( ! HasLayoutData() )
140 FillLayoutData();
141 return mxLayoutData ? mxLayoutData->GetIndexForPoint( rPoint ) : -1;
144 Pair ControlLayoutData::GetLineStartEnd( tools::Long nLine ) const
146 Pair aPair( -1, -1 );
148 int nDisplayLines = m_aLineIndices.size();
149 if( nLine >= 0 && nLine < nDisplayLines )
151 aPair.A() = m_aLineIndices[nLine];
152 if( nLine+1 < nDisplayLines )
153 aPair.B() = m_aLineIndices[nLine+1]-1;
154 else
155 aPair.B() = m_aDisplayText.getLength()-1;
157 else if( nLine == 0 && nDisplayLines == 0 && !m_aDisplayText.isEmpty() )
159 // special case for single line controls so the implementations
160 // in that case do not have to fill in the line indices
161 aPair.A() = 0;
162 aPair.B() = m_aDisplayText.getLength()-1;
164 return aPair;
167 Pair Control::GetLineStartEnd( tools::Long nLine ) const
169 if( !HasLayoutData() )
170 FillLayoutData();
171 return mxLayoutData ? mxLayoutData->GetLineStartEnd( nLine ) : Pair( -1, -1 );
174 tools::Long ControlLayoutData::ToRelativeLineIndex( tools::Long nIndex ) const
176 // is the index sensible at all ?
177 if( nIndex >= 0 && nIndex < m_aDisplayText.getLength() )
179 int nDisplayLines = m_aLineIndices.size();
180 // if only 1 line exists, then absolute and relative index are
181 // identical -> do nothing
182 if( nDisplayLines > 1 )
184 int nLine;
185 for( nLine = nDisplayLines-1; nLine >= 0; nLine-- )
187 if( m_aLineIndices[nLine] <= nIndex )
189 nIndex -= m_aLineIndices[nLine];
190 break;
193 if( nLine < 0 )
195 SAL_WARN_IF( nLine < 0, "vcl", "ToRelativeLineIndex failed" );
196 nIndex = -1;
200 else
201 nIndex = -1;
203 return nIndex;
206 tools::Long Control::ToRelativeLineIndex( tools::Long nIndex ) const
208 if( !HasLayoutData() )
209 FillLayoutData();
210 return mxLayoutData ? mxLayoutData->ToRelativeLineIndex( nIndex ) : -1;
213 OUString Control::GetDisplayText() const
215 if( !HasLayoutData() )
216 FillLayoutData();
217 return mxLayoutData ? mxLayoutData->m_aDisplayText : GetText();
220 bool Control::FocusWindowBelongsToControl(const vcl::Window* pFocusWin) const
222 return ImplIsWindowOrChild(pFocusWin);
225 bool Control::EventNotify( NotifyEvent& rNEvt )
227 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
229 if ( !mbHasControlFocus )
231 mbHasControlFocus = true;
232 CompatStateChanged( StateChangedType::ControlFocus );
233 if ( ImplCallEventListenersAndHandler( VclEventId::ControlGetFocus, {} ) )
234 // been destroyed within the handler
235 return true;
238 else
240 if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
242 vcl::Window* pFocusWin = Application::GetFocusWindow();
243 if ( !pFocusWin || !FocusWindowBelongsToControl(pFocusWin) )
245 mbHasControlFocus = false;
246 CompatStateChanged( StateChangedType::ControlFocus );
247 if ( ImplCallEventListenersAndHandler( VclEventId::ControlLoseFocus, [this] () { maLoseFocusHdl.Call(*this); } ) )
248 // been destroyed within the handler
249 return true;
253 return Window::EventNotify( rNEvt );
256 void Control::StateChanged( StateChangedType nStateChange )
258 if( nStateChange == StateChangedType::InitShow ||
259 nStateChange == StateChangedType::Visible ||
260 nStateChange == StateChangedType::Zoom ||
261 nStateChange == StateChangedType::ControlFont
264 ImplClearLayoutData();
266 Window::StateChanged( nStateChange );
269 void Control::AppendLayoutData( const Control& rSubControl ) const
271 if( !rSubControl.HasLayoutData() )
272 rSubControl.FillLayoutData();
273 if( !rSubControl.HasLayoutData() || rSubControl.mxLayoutData->m_aDisplayText.isEmpty() )
274 return;
276 tools::Long nCurrentIndex = mxLayoutData->m_aDisplayText.getLength();
277 mxLayoutData->m_aDisplayText += rSubControl.mxLayoutData->m_aDisplayText;
278 int nLines = rSubControl.mxLayoutData->m_aLineIndices.size();
279 int n;
280 mxLayoutData->m_aLineIndices.push_back( nCurrentIndex );
281 for( n = 1; n < nLines; n++ )
282 mxLayoutData->m_aLineIndices.push_back( rSubControl.mxLayoutData->m_aLineIndices[n] + nCurrentIndex );
283 int nRectangles = rSubControl.mxLayoutData->m_aUnicodeBoundRects.size();
284 tools::Rectangle aRel = rSubControl.GetWindowExtentsRelative(*this);
285 for( n = 0; n < nRectangles; n++ )
287 tools::Rectangle aRect = rSubControl.mxLayoutData->m_aUnicodeBoundRects[n];
288 aRect.Move( aRel.Left(), aRel.Top() );
289 mxLayoutData->m_aUnicodeBoundRects.push_back( aRect );
293 void Control::CallEventListeners( VclEventId nEvent, void* pData)
295 VclPtr<Control> xThis(this);
296 UITestLogger::getInstance().logAction(xThis, nEvent);
298 vcl::Window::CallEventListeners(nEvent, pData);
301 bool Control::ImplCallEventListenersAndHandler( VclEventId nEvent, std::function<void()> const & callHandler )
303 VclPtr<Control> xThis(this);
305 Control::CallEventListeners( nEvent );
307 if ( !xThis->isDisposed() )
309 if (callHandler)
311 callHandler();
314 if ( !xThis->isDisposed() )
315 return false;
317 return true;
320 void Control::SetLayoutDataParent( const Control* pParent ) const
322 if( HasLayoutData() )
323 mxLayoutData->m_pParent = pParent;
326 void Control::ImplClearLayoutData() const
328 mxLayoutData.reset();
331 void Control::ImplDrawFrame( OutputDevice* pDev, tools::Rectangle& rRect )
333 // use a deco view to draw the frame
334 // However, since there happens a lot of magic there, we need to fake some (style) settings
335 // on the device
336 AllSettings aOriginalSettings( pDev->GetSettings() );
338 AllSettings aNewSettings( aOriginalSettings );
339 StyleSettings aStyle( aNewSettings.GetStyleSettings() );
341 // The *only known* clients of the Draw methods of the various VCL-controls are form controls:
342 // During print preview, and during printing, Draw is called. Thus, drawing always happens with a
343 // mono (colored) border
344 aStyle.SetOptions( aStyle.GetOptions() | StyleSettingsOptions::Mono );
345 aStyle.SetMonoColor( GetSettings().GetStyleSettings().GetMonoColor() );
347 aNewSettings.SetStyleSettings( aStyle );
348 // #i67023# do not call data changed listeners for this fake
349 // since they may understandably invalidate on settings changed
350 pDev->OutputDevice::SetSettings( aNewSettings );
352 DecorationView aDecoView( pDev );
353 rRect = aDecoView.DrawFrame( rRect, DrawFrameStyle::Out, DrawFrameFlags::WindowBorder );
355 pDev->OutputDevice::SetSettings( aOriginalSettings );
358 void Control::SetShowAccelerator(bool bVal)
360 mbShowAccelerator = bVal;
363 ControlLayoutData::~ControlLayoutData()
365 if( m_pParent )
366 m_pParent->ImplClearLayoutData();
369 Size Control::GetOptimalSize() const
371 return Size( GetTextWidth( GetText() ) + 2 * 12,
372 GetTextHeight() + 2 * 6 );
375 void Control::SetReferenceDevice( OutputDevice* _referenceDevice )
377 if ( mpReferenceDevice == _referenceDevice )
378 return;
380 mpReferenceDevice = _referenceDevice;
381 Invalidate();
384 OutputDevice* Control::GetReferenceDevice() const
386 // tdf#118377 It can happen that mpReferenceDevice is already disposed and
387 // stays disposed (see task, even when Dialog is closed). I have no idea if
388 // this may be very bad - someone who knows more about lifetime of OutputDevice's
389 // will have to decide.
390 // To secure this, I changed all accesses to mpControlData->mpReferenceDevice to
391 // use Control::GetReferenceDevice() - only use mpControlData->mpReferenceDevice
392 // inside Control::SetReferenceDevice and Control::GetReferenceDevice().
393 // Control::GetReferenceDevice() will now reset mpReferenceDevice if it is already
394 // disposed. This way all usages will do a kind of 'test-and-get' call.
395 if(nullptr != mpReferenceDevice && mpReferenceDevice->isDisposed())
397 const_cast<Control*>(this)->SetReferenceDevice(nullptr);
400 return mpReferenceDevice;
403 const vcl::Font& Control::GetCanonicalFont( const StyleSettings& _rStyle ) const
405 return _rStyle.GetLabelFont();
408 const Color& Control::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
410 return _rStyle.GetLabelTextColor();
413 void Control::ApplySettings(vcl::RenderContext& rRenderContext)
415 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
417 ApplyControlFont(rRenderContext, GetCanonicalFont(rStyleSettings));
419 ApplyControlForeground(rRenderContext, GetCanonicalTextColor(rStyleSettings));
420 rRenderContext.SetTextFillColor();
423 void Control::ImplInitSettings()
425 ApplySettings(*GetOutDev());
428 tools::Rectangle Control::DrawControlText( OutputDevice& _rTargetDevice, const tools::Rectangle& rRect, const OUString& _rStr,
429 DrawTextFlags _nStyle, std::vector< tools::Rectangle >* _pVector, OUString* _pDisplayText, const Size* i_pDeviceSize ) const
431 OUString rPStr = _rStr;
432 DrawTextFlags nPStyle = _nStyle;
434 bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
436 if (autoacc && !mbShowAccelerator)
437 rPStr = removeMnemonicFromString( _rStr );
439 if( !GetReferenceDevice() || ( GetReferenceDevice() == &_rTargetDevice ) )
441 const tools::Rectangle aRet = _rTargetDevice.GetTextRect(rRect, rPStr, nPStyle);
442 _rTargetDevice.DrawText(aRet, rPStr, nPStyle, _pVector, _pDisplayText);
443 return aRet;
446 ControlTextRenderer aRenderer( *this, _rTargetDevice, *GetReferenceDevice() );
447 return aRenderer.DrawText(rRect, rPStr, nPStyle, _pVector, _pDisplayText, i_pDeviceSize);
450 tools::Rectangle Control::GetControlTextRect( OutputDevice& _rTargetDevice, const tools::Rectangle & rRect,
451 const OUString& _rStr, DrawTextFlags _nStyle, Size* o_pDeviceSize ) const
453 OUString rPStr = _rStr;
454 DrawTextFlags nPStyle = _nStyle;
456 bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
458 if (autoacc && !mbShowAccelerator)
459 rPStr = removeMnemonicFromString( _rStr );
461 if ( !GetReferenceDevice() || ( GetReferenceDevice() == &_rTargetDevice ) )
463 tools::Rectangle aRet = _rTargetDevice.GetTextRect( rRect, rPStr, nPStyle );
464 if (o_pDeviceSize)
466 *o_pDeviceSize = aRet.GetSize();
468 return aRet;
471 ControlTextRenderer aRenderer( *this, _rTargetDevice, *GetReferenceDevice() );
472 return aRenderer.GetTextRect(rRect, rPStr, nPStyle, o_pDeviceSize);
475 Font
476 Control::GetUnzoomedControlPointFont() const
478 Font aFont(GetCanonicalFont(GetSettings().GetStyleSettings()));
479 if (IsControlFont())
480 aFont.Merge(GetControlFont());
481 return aFont;
484 void Control::LogicInvalidate(const tools::Rectangle* pRectangle)
486 VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier();
487 if (!pParent || !dynamic_cast<vcl::DocWindow*>(GetParent()))
489 // if control doesn't belong to a DocWindow, the overridden base class
490 // method has to be invoked
491 Window::LogicInvalidate(pRectangle);
492 return;
495 // avoid endless paint/invalidate loop in Impress
496 if (comphelper::LibreOfficeKit::isTiledPainting())
497 return;
499 tools::Rectangle aResultRectangle;
500 if (!pRectangle)
502 // we have to invalidate the whole control area not the whole document
503 aResultRectangle = PixelToLogic(tools::Rectangle(GetPosPixel(), GetSizePixel()), MapMode(MapUnit::MapTwip));
505 else
507 aResultRectangle = OutputDevice::LogicToLogic(*pRectangle, GetMapMode(), MapMode(MapUnit::MapTwip));
510 pParent->GetLOKNotifier()->notifyInvalidation(&aResultRectangle);
513 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */