1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
35 #include <controldata.hxx>
39 void Control::ImplInitControlData()
41 mbHasControlFocus
= false;
42 mbShowAccelerator
= false;
43 mpControlData
= new ImplControlData
;
46 Control::Control( WindowType nType
) :
49 ImplInitControlData();
52 Control::Control( vcl::Window
* pParent
, WinBits nStyle
) :
53 Window( WINDOW_CONTROL
)
55 ImplInitControlData();
56 ImplInit( pParent
, nStyle
, nullptr );
64 void Control::dispose()
67 mpControlData
= nullptr;
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();
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() )
120 return mpControlData
->mpLayoutData
? mpControlData
->mpLayoutData
->GetCharacterBounds( nIndex
) : Rectangle();
123 long ControlLayoutData::GetIndexForPoint( const Point
& rPoint
) const
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())
140 long Control::GetIndexForPoint( const Point
& rPoint
) const
142 if( ! HasLayoutData() )
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() )
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;
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
173 aPair
.B() = m_aDisplayText
.getLength()-1;
178 Pair
Control::GetLineStartEnd( long nLine
) const
180 if( !HasLayoutData() )
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 )
196 for( nLine
= nDisplayLines
-1; nLine
>= 0; nLine
-- )
198 if( m_aLineIndices
[nLine
] <= nIndex
)
200 nIndex
-= m_aLineIndices
[nLine
];
206 SAL_WARN_IF( nLine
< 0, "vcl", "ToRelativeLineIndex failed" );
217 long Control::ToRelativeLineIndex( long nIndex
) const
219 if( !HasLayoutData() )
221 return mpControlData
->mpLayoutData
? mpControlData
->mpLayoutData
->ToRelativeLineIndex( nIndex
) : -1;
224 OUString
Control::GetDisplayText() const
226 if( !HasLayoutData() )
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
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
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
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() )
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();
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() )
317 if ( !xThis
->IsDisposed() )
323 void Control::SetLayoutDataParent( const Control
* pParent
) const
325 if( HasLayoutData() )
326 mpControlData
->mpLayoutData
->m_pParent
= pParent
;
329 void Control::ImplClearLayoutData() const
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
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()
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
)
384 mpControlData
->mpReferenceDevice
= _referenceDevice
;
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
);
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
);
465 *o_pDeviceSize
= aRet
.GetSize();
470 ControlTextRenderer
aRenderer( *this, _rTargetDevice
, *mpControlData
->mpReferenceDevice
);
471 return aRenderer
.GetTextRect(rRect
, rPStr
, nPStyle
, o_pDeviceSize
);
475 Control::GetUnzoomedControlPointFont() const
477 Font
aFont(GetCanonicalFont(GetSettings().GetStyleSettings()));
479 aFont
.Merge(GetControlFont());
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */