build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / ctrl.cxx
blob5dd0bc1d822f11d3c748b59aacde98fbe8e0af18
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/processfactory.hxx>
21 #include <comphelper/lok.hxx>
23 #include <tools/rc.h>
24 #include <vcl/svapp.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/ctrl.hxx>
27 #include <vcl/floatwin.hxx>
28 #include <vcl/decoview.hxx>
29 #include <vcl/dialog.hxx>
30 #include <vcl/salnativewidgets.hxx>
31 #include <vcl/settings.hxx>
33 #include <textlayout.hxx>
34 #include <svdata.hxx>
35 #include <controldata.hxx>
37 using namespace vcl;
39 void Control::ImplInitControlData()
41 mbHasControlFocus = false;
42 mbShowAccelerator = false;
43 mpControlData = new ImplControlData;
46 Control::Control( WindowType nType ) :
47 Window( nType )
49 ImplInitControlData();
52 Control::Control( vcl::Window* pParent, WinBits nStyle ) :
53 Window( WINDOW_CONTROL )
55 ImplInitControlData();
56 ImplInit( pParent, nStyle, nullptr );
59 Control::~Control()
61 disposeOnce();
64 void Control::dispose()
66 delete mpControlData;
67 mpControlData = nullptr;
68 Window::dispose();
71 void Control::EnableRTL( bool bEnable )
73 // convenience: for controls also switch layout mode
74 SetLayoutMode( bEnable ? ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft :
75 ComplexTextLayoutFlags::TextOriginLeft );
76 CompatStateChanged( StateChangedType::Mirroring );
77 OutputDevice::EnableRTL(bEnable);
80 void Control::Resize()
82 ImplClearLayoutData();
83 Window::Resize();
86 void Control::FillLayoutData() const
90 void Control::CreateLayoutData() const
92 SAL_WARN_IF( mpControlData->mpLayoutData, "vcl", "Control::CreateLayoutData: should be called with non-existent layout data only!" );
93 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
96 bool Control::HasLayoutData() const
98 return mpControlData && mpControlData->mpLayoutData != nullptr;
101 void Control::SetText( const OUString& rStr )
103 ImplClearLayoutData();
104 Window::SetText( rStr );
107 ControlLayoutData::ControlLayoutData() : m_pParent( nullptr )
111 Rectangle ControlLayoutData::GetCharacterBounds( long nIndex ) const
113 return (nIndex >= 0 && nIndex < (long) m_aUnicodeBoundRects.size()) ? m_aUnicodeBoundRects[ nIndex ] : Rectangle();
116 Rectangle Control::GetCharacterBounds( long nIndex ) const
118 if( !HasLayoutData() )
119 FillLayoutData();
120 return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->GetCharacterBounds( nIndex ) : Rectangle();
123 long ControlLayoutData::GetIndexForPoint( const Point& rPoint ) const
125 long nIndex = -1;
126 for( long i = m_aUnicodeBoundRects.size()-1; i >= 0; i-- )
128 Point aTopLeft = m_aUnicodeBoundRects[i].TopLeft();
129 Point aBottomRight = m_aUnicodeBoundRects[i].BottomRight();
130 if (rPoint.X() >= aTopLeft.X() && rPoint.Y() >= aTopLeft.Y() &&
131 rPoint.X() <= aBottomRight.X() && rPoint.Y() <= aBottomRight.Y())
133 nIndex = i;
134 break;
137 return nIndex;
140 long Control::GetIndexForPoint( const Point& rPoint ) const
142 if( ! HasLayoutData() )
143 FillLayoutData();
144 return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->GetIndexForPoint( rPoint ) : -1;
147 long ControlLayoutData::GetLineCount() const
149 long nLines = m_aLineIndices.size();
150 if( nLines == 0 && !m_aDisplayText.isEmpty() )
151 nLines = 1;
152 return nLines;
155 Pair ControlLayoutData::GetLineStartEnd( long nLine ) const
157 Pair aPair( -1, -1 );
159 int nDisplayLines = m_aLineIndices.size();
160 if( nLine >= 0 && nLine < nDisplayLines )
162 aPair.A() = m_aLineIndices[nLine];
163 if( nLine+1 < nDisplayLines )
164 aPair.B() = m_aLineIndices[nLine+1]-1;
165 else
166 aPair.B() = m_aDisplayText.getLength()-1;
168 else if( nLine == 0 && nDisplayLines == 0 && !m_aDisplayText.isEmpty() )
170 // special case for single line controls so the implementations
171 // in that case do not have to fill in the line indices
172 aPair.A() = 0;
173 aPair.B() = m_aDisplayText.getLength()-1;
175 return aPair;
178 Pair Control::GetLineStartEnd( long nLine ) const
180 if( !HasLayoutData() )
181 FillLayoutData();
182 return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->GetLineStartEnd( nLine ) : Pair( -1, -1 );
185 long ControlLayoutData::ToRelativeLineIndex( long nIndex ) const
187 // is the index sensible at all ?
188 if( nIndex >= 0 && nIndex < m_aDisplayText.getLength() )
190 int nDisplayLines = m_aLineIndices.size();
191 // if only 1 line exists, then absolute and relative index are
192 // identical -> do nothing
193 if( nDisplayLines > 1 )
195 int nLine;
196 for( nLine = nDisplayLines-1; nLine >= 0; nLine-- )
198 if( m_aLineIndices[nLine] <= nIndex )
200 nIndex -= m_aLineIndices[nLine];
201 break;
204 if( nLine < 0 )
206 SAL_WARN_IF( nLine < 0, "vcl", "ToRelativeLineIndex failed" );
207 nIndex = -1;
211 else
212 nIndex = -1;
214 return nIndex;
217 long Control::ToRelativeLineIndex( long nIndex ) const
219 if( !HasLayoutData() )
220 FillLayoutData();
221 return mpControlData->mpLayoutData ? mpControlData->mpLayoutData->ToRelativeLineIndex( nIndex ) : -1;
224 OUString Control::GetDisplayText() const
226 if( !HasLayoutData() )
227 FillLayoutData();
228 return mpControlData->mpLayoutData ? OUString(mpControlData->mpLayoutData->m_aDisplayText) : GetText();
231 bool Control::EventNotify( NotifyEvent& rNEvt )
233 // tdf#91081 if control is not valid, skip the emission - chaining to the parent
234 if (mpControlData)
236 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
238 if ( !mbHasControlFocus )
240 mbHasControlFocus = true;
241 CompatStateChanged( StateChangedType::ControlFocus );
242 if ( ImplCallEventListenersAndHandler( VCLEVENT_CONTROL_GETFOCUS, [this] () { maGetFocusHdl.Call(*this); } ) )
243 // been destroyed within the handler
244 return true;
247 else
249 if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
251 vcl::Window* pFocusWin = Application::GetFocusWindow();
252 if ( !pFocusWin || !ImplIsWindowOrChild( pFocusWin ) )
254 mbHasControlFocus = false;
255 CompatStateChanged( StateChangedType::ControlFocus );
256 if ( ImplCallEventListenersAndHandler( VCLEVENT_CONTROL_LOSEFOCUS, [this] () { maLoseFocusHdl.Call(*this); } ) )
257 // been destroyed within the handler
258 return true;
263 return Window::EventNotify( rNEvt );
266 void Control::StateChanged( StateChangedType nStateChange )
268 if( nStateChange == StateChangedType::InitShow ||
269 nStateChange == StateChangedType::Visible ||
270 nStateChange == StateChangedType::Zoom ||
271 nStateChange == StateChangedType::Border ||
272 nStateChange == StateChangedType::ControlFont
275 ImplClearLayoutData();
277 Window::StateChanged( nStateChange );
280 void Control::AppendLayoutData( const Control& rSubControl ) const
282 if( !rSubControl.HasLayoutData() )
283 rSubControl.FillLayoutData();
284 if( !rSubControl.HasLayoutData() || rSubControl.mpControlData->mpLayoutData->m_aDisplayText.isEmpty() )
285 return;
287 long nCurrentIndex = mpControlData->mpLayoutData->m_aDisplayText.getLength();
288 mpControlData->mpLayoutData->m_aDisplayText += rSubControl.mpControlData->mpLayoutData->m_aDisplayText;
289 int nLines = rSubControl.mpControlData->mpLayoutData->m_aLineIndices.size();
290 int n;
291 mpControlData->mpLayoutData->m_aLineIndices.push_back( nCurrentIndex );
292 for( n = 1; n < nLines; n++ )
293 mpControlData->mpLayoutData->m_aLineIndices.push_back( rSubControl.mpControlData->mpLayoutData->m_aLineIndices[n] + nCurrentIndex );
294 int nRectangles = rSubControl.mpControlData->mpLayoutData->m_aUnicodeBoundRects.size();
295 Rectangle aRel = const_cast<Control&>(rSubControl).GetWindowExtentsRelative( const_cast<Control*>(this) );
296 for( n = 0; n < nRectangles; n++ )
298 Rectangle aRect = rSubControl.mpControlData->mpLayoutData->m_aUnicodeBoundRects[n];
299 aRect.Move( aRel.Left(), aRel.Top() );
300 mpControlData->mpLayoutData->m_aUnicodeBoundRects.push_back( aRect );
304 bool Control::ImplCallEventListenersAndHandler( sal_uLong nEvent, std::function<void()> const & callHandler )
306 VclPtr<Control> xThis(this);
308 CallEventListeners( nEvent );
310 if ( !xThis->IsDisposed() )
312 if (callHandler)
314 callHandler();
317 if ( !xThis->IsDisposed() )
318 return false;
320 return true;
323 void Control::SetLayoutDataParent( const Control* pParent ) const
325 if( HasLayoutData() )
326 mpControlData->mpLayoutData->m_pParent = pParent;
329 void Control::ImplClearLayoutData() const
331 if (mpControlData)
332 mpControlData->mpLayoutData.reset();
335 void Control::ImplDrawFrame( OutputDevice* pDev, Rectangle& rRect )
337 // use a deco view to draw the frame
338 // However, since there happens a lot of magic there, we need to fake some (style) settings
339 // on the device
340 AllSettings aOriginalSettings( pDev->GetSettings() );
342 AllSettings aNewSettings( aOriginalSettings );
343 StyleSettings aStyle( aNewSettings.GetStyleSettings() );
345 // The *only known* clients of the Draw methods of the various VCL-controls are form controls:
346 // During print preview, and during printing, Draw is called. Thus, drawing always happens with a
347 // mono (colored) border
348 aStyle.SetOptions( aStyle.GetOptions() | StyleSettingsOptions::Mono );
349 aStyle.SetMonoColor( GetSettings().GetStyleSettings().GetMonoColor() );
351 aNewSettings.SetStyleSettings( aStyle );
352 // #i67023# do not call data changed listeners for this fake
353 // since they may understandably invalidate on settings changed
354 pDev->OutputDevice::SetSettings( aNewSettings );
356 DecorationView aDecoView( pDev );
357 rRect = aDecoView.DrawFrame( rRect, DrawFrameStyle::Out, DrawFrameFlags::WindowBorder );
359 pDev->OutputDevice::SetSettings( aOriginalSettings );
362 void Control::SetShowAccelerator(bool bVal)
364 mbShowAccelerator = bVal;
367 ControlLayoutData::~ControlLayoutData()
369 if( m_pParent )
370 m_pParent->ImplClearLayoutData();
373 Size Control::GetOptimalSize() const
375 return Size( GetTextWidth( GetText() ) + 2 * 12,
376 GetTextHeight() + 2 * 6 );
379 void Control::SetReferenceDevice( OutputDevice* _referenceDevice )
381 if ( mpControlData->mpReferenceDevice == _referenceDevice )
382 return;
384 mpControlData->mpReferenceDevice = _referenceDevice;
385 Invalidate();
388 OutputDevice* Control::GetReferenceDevice() const
390 return mpControlData->mpReferenceDevice;
393 const vcl::Font& Control::GetCanonicalFont( const StyleSettings& _rStyle ) const
395 return _rStyle.GetLabelFont();
398 const Color& Control::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
400 return _rStyle.GetLabelTextColor();
403 void Control::ApplySettings(vcl::RenderContext& rRenderContext)
405 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
407 vcl::Font rFont(GetCanonicalFont(rStyleSettings));
408 ApplyControlFont(rRenderContext, rFont);
410 ApplyControlForeground(rRenderContext, GetCanonicalTextColor(rStyleSettings));
411 rRenderContext.SetTextFillColor();
414 void Control::ImplInitSettings(const bool, const bool)
416 ApplySettings(*this);
419 Rectangle Control::DrawControlText( OutputDevice& _rTargetDevice, const Rectangle& rRect, const OUString& _rStr,
420 DrawTextFlags _nStyle, MetricVector* _pVector, OUString* _pDisplayText, const Size* i_pDeviceSize ) const
422 OUString rPStr = _rStr;
423 DrawTextFlags nPStyle = _nStyle;
425 bool accel = ImplGetSVData()->maNWFData.mbEnableAccel;
426 bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
428 if (!accel || (autoacc && !mbShowAccelerator))
430 rPStr = GetNonMnemonicString( _rStr );
431 nPStyle &= ~DrawTextFlags::HideMnemonic;
434 if ( !mpControlData->mpReferenceDevice || ( mpControlData->mpReferenceDevice == &_rTargetDevice ) )
436 const Rectangle aRet = _rTargetDevice.GetTextRect(rRect, rPStr, nPStyle);
437 _rTargetDevice.DrawText(aRet, rPStr, nPStyle, _pVector, _pDisplayText);
438 return aRet;
441 ControlTextRenderer aRenderer( *this, _rTargetDevice, *mpControlData->mpReferenceDevice );
442 return aRenderer.DrawText(rRect, rPStr, nPStyle, _pVector, _pDisplayText, i_pDeviceSize);
445 Rectangle Control::GetControlTextRect( OutputDevice& _rTargetDevice, const Rectangle & rRect,
446 const OUString& _rStr, DrawTextFlags _nStyle, Size* o_pDeviceSize ) const
448 OUString rPStr = _rStr;
449 DrawTextFlags nPStyle = _nStyle;
451 bool accel = ImplGetSVData()->maNWFData.mbEnableAccel;
452 bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
454 if (!accel || (autoacc && !mbShowAccelerator))
456 rPStr = GetNonMnemonicString( _rStr );
457 nPStyle &= ~DrawTextFlags::HideMnemonic;
460 if ( !mpControlData->mpReferenceDevice || ( mpControlData->mpReferenceDevice == &_rTargetDevice ) )
462 Rectangle aRet = _rTargetDevice.GetTextRect( rRect, rPStr, nPStyle );
463 if (o_pDeviceSize)
465 *o_pDeviceSize = aRet.GetSize();
467 return aRet;
470 ControlTextRenderer aRenderer( *this, _rTargetDevice, *mpControlData->mpReferenceDevice );
471 return aRenderer.GetTextRect(rRect, rPStr, nPStyle, o_pDeviceSize);
474 Font
475 Control::GetUnzoomedControlPointFont() const
477 Font aFont(GetCanonicalFont(GetSettings().GetStyleSettings()));
478 if (IsControlFont())
479 aFont.Merge(GetControlFont());
480 return aFont;
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */