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/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>
37 void Control::ImplInitControlData()
39 mbHasControlFocus
= false;
40 mbShowAccelerator
= false;
43 Control::Control( WindowType nType
) :
46 ImplInitControlData();
49 Control::Control( vcl::Window
* pParent
, WinBits nStyle
) :
50 Window( WindowType::CONTROL
)
52 ImplInitControlData();
53 ImplInit( pParent
, nStyle
, nullptr );
61 void Control::dispose()
64 mpReferenceDevice
.clear();
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();
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() )
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())
137 tools::Long
Control::GetIndexForPoint( const Point
& rPoint
) const
139 if( ! HasLayoutData() )
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;
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
162 aPair
.B() = m_aDisplayText
.getLength()-1;
167 Pair
Control::GetLineStartEnd( tools::Long nLine
) const
169 if( !HasLayoutData() )
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 )
185 for( nLine
= nDisplayLines
-1; nLine
>= 0; nLine
-- )
187 if( m_aLineIndices
[nLine
] <= nIndex
)
189 nIndex
-= m_aLineIndices
[nLine
];
195 SAL_WARN_IF( nLine
< 0, "vcl", "ToRelativeLineIndex failed" );
206 tools::Long
Control::ToRelativeLineIndex( tools::Long nIndex
) const
208 if( !HasLayoutData() )
210 return mxLayoutData
? mxLayoutData
->ToRelativeLineIndex( nIndex
) : -1;
213 OUString
Control::GetDisplayText() const
215 if( !HasLayoutData() )
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
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
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() )
276 tools::Long nCurrentIndex
= mxLayoutData
->m_aDisplayText
.getLength();
277 mxLayoutData
->m_aDisplayText
+= rSubControl
.mxLayoutData
->m_aDisplayText
;
278 int nLines
= rSubControl
.mxLayoutData
->m_aLineIndices
.size();
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() )
314 if ( !xThis
->isDisposed() )
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
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()
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
)
380 mpReferenceDevice
= _referenceDevice
;
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
);
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
);
466 *o_pDeviceSize
= aRet
.GetSize();
471 ControlTextRenderer
aRenderer( *this, _rTargetDevice
, *GetReferenceDevice() );
472 return aRenderer
.GetTextRect(rRect
, rPStr
, nPStyle
, o_pDeviceSize
);
476 Control::GetUnzoomedControlPointFont() const
478 Font
aFont(GetCanonicalFont(GetSettings().GetStyleSettings()));
480 aFont
.Merge(GetControlFont());
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
);
495 // avoid endless paint/invalidate loop in Impress
496 if (comphelper::LibreOfficeKit::isTiledPainting())
499 tools::Rectangle aResultRectangle
;
502 // we have to invalidate the whole control area not the whole document
503 aResultRectangle
= PixelToLogic(tools::Rectangle(GetPosPixel(), GetSizePixel()), MapMode(MapUnit::MapTwip
));
507 aResultRectangle
= OutputDevice::LogicToLogic(*pRectangle
, GetMapMode(), MapMode(MapUnit::MapTwip
));
510 pParent
->GetLOKNotifier()->notifyInvalidation(&aResultRectangle
);
513 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */