/src/libreoffice/vcl/source/control/ctrl.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <comphelper/lok.hxx> |
21 | | #include <tools/mapunit.hxx> |
22 | | |
23 | | #include <vcl/DocWindow.hxx> |
24 | | #include <vcl/ctrl.hxx> |
25 | | #include <vcl/decoview.hxx> |
26 | | #include <vcl/event.hxx> |
27 | | #include <vcl/mnemonic.hxx> |
28 | | |
29 | | #include <svdata.hxx> |
30 | | #include <textlayout.hxx> |
31 | | |
32 | | using namespace vcl; |
33 | | |
34 | | void Control::ImplInitControlData() |
35 | 0 | { |
36 | 0 | mbHasControlFocus = false; |
37 | 0 | mbShowAccelerator = false; |
38 | 0 | } |
39 | | |
40 | | Control::Control(WindowType eType) |
41 | 0 | : Window(eType) |
42 | 0 | { |
43 | 0 | ImplInitControlData(); |
44 | 0 | } Unexecuted instantiation: Control::Control(WindowType) Unexecuted instantiation: Control::Control(WindowType) |
45 | | |
46 | | Control::Control(vcl::Window* pParent, WinBits eStyle) |
47 | 0 | : Window(WindowType::CONTROL) |
48 | 0 | { |
49 | 0 | ImplInitControlData(); |
50 | 0 | ImplInit(pParent, eStyle, nullptr); |
51 | 0 | } Unexecuted instantiation: Control::Control(vcl::Window*, long) Unexecuted instantiation: Control::Control(vcl::Window*, long) |
52 | | |
53 | | Control::~Control() |
54 | 0 | { |
55 | 0 | disposeOnce(); |
56 | 0 | } |
57 | | |
58 | | void Control::dispose() |
59 | 0 | { |
60 | 0 | mxLayoutData.reset(); |
61 | 0 | mpReferenceDevice.reset(); |
62 | 0 | Window::dispose(); |
63 | 0 | } |
64 | | |
65 | | void Control::EnableRTL( bool bEnable ) |
66 | 0 | { |
67 | | // convenience: for controls also switch layout mode |
68 | 0 | GetOutDev()->SetLayoutMode( bEnable ? vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft : |
69 | 0 | vcl::text::ComplexTextLayoutFlags::TextOriginLeft ); |
70 | 0 | CompatStateChanged( StateChangedType::Mirroring ); |
71 | 0 | Window::EnableRTL(bEnable); |
72 | 0 | } |
73 | | |
74 | | void Control::Resize() |
75 | 0 | { |
76 | 0 | ImplClearLayoutData(); |
77 | 0 | Window::Resize(); |
78 | 0 | } |
79 | | |
80 | | void Control::FillLayoutData() const |
81 | 0 | { |
82 | 0 | } |
83 | | |
84 | | void Control::CreateLayoutData() const |
85 | 0 | { |
86 | 0 | SAL_WARN_IF( mxLayoutData, "vcl", "Control::CreateLayoutData: should be called with non-existent layout data only!" ); |
87 | 0 | mxLayoutData.emplace(); |
88 | 0 | } |
89 | | |
90 | | bool Control::HasLayoutData() const |
91 | 0 | { |
92 | 0 | return bool(mxLayoutData); |
93 | 0 | } |
94 | | |
95 | | void Control::SetText( const OUString& rStr ) |
96 | 0 | { |
97 | 0 | ImplClearLayoutData(); |
98 | 0 | Window::SetText( rStr ); |
99 | 0 | } |
100 | | |
101 | 0 | ControlLayoutData::ControlLayoutData() : m_pParent( nullptr ) |
102 | 0 | { |
103 | 0 | } |
104 | | |
105 | | tools::Rectangle ControlLayoutData::GetCharacterBounds( tools::Long nIndex ) const |
106 | 0 | { |
107 | 0 | return (nIndex >= 0 && o3tl::make_unsigned(nIndex) < m_aUnicodeBoundRects.size()) ? m_aUnicodeBoundRects[ nIndex ] : tools::Rectangle(); |
108 | 0 | } |
109 | | |
110 | | tools::Rectangle Control::GetCharacterBounds( tools::Long nIndex ) const |
111 | 0 | { |
112 | 0 | if( !HasLayoutData() ) |
113 | 0 | FillLayoutData(); |
114 | 0 | return mxLayoutData ? mxLayoutData->GetCharacterBounds( nIndex ) : tools::Rectangle(); |
115 | 0 | } |
116 | | |
117 | | tools::Long ControlLayoutData::GetIndexForPoint( const Point& rPoint ) const |
118 | 0 | { |
119 | 0 | tools::Long nIndex = -1; |
120 | 0 | for( tools::Long i = m_aUnicodeBoundRects.size()-1; i >= 0; i-- ) |
121 | 0 | { |
122 | 0 | Point aTopLeft = m_aUnicodeBoundRects[i].TopLeft(); |
123 | 0 | Point aBottomRight = m_aUnicodeBoundRects[i].BottomRight(); |
124 | 0 | if (rPoint.X() >= aTopLeft.X() && rPoint.Y() >= aTopLeft.Y() && |
125 | 0 | rPoint.X() <= aBottomRight.X() && rPoint.Y() <= aBottomRight.Y()) |
126 | 0 | { |
127 | 0 | nIndex = i; |
128 | 0 | break; |
129 | 0 | } |
130 | 0 | } |
131 | 0 | return nIndex; |
132 | 0 | } |
133 | | |
134 | | tools::Long Control::GetIndexForPoint( const Point& rPoint ) const |
135 | 0 | { |
136 | 0 | if( ! HasLayoutData() ) |
137 | 0 | FillLayoutData(); |
138 | 0 | return mxLayoutData ? mxLayoutData->GetIndexForPoint( rPoint ) : -1; |
139 | 0 | } |
140 | | |
141 | | Pair ControlLayoutData::GetLineStartEnd( tools::Long nLine ) const |
142 | 0 | { |
143 | 0 | Pair aPair( -1, -1 ); |
144 | |
|
145 | 0 | int nDisplayLines = m_aLineIndices.size(); |
146 | 0 | if( nLine >= 0 && nLine < nDisplayLines ) |
147 | 0 | { |
148 | 0 | aPair.A() = m_aLineIndices[nLine]; |
149 | 0 | if( nLine+1 < nDisplayLines ) |
150 | 0 | aPair.B() = m_aLineIndices[nLine+1]-1; |
151 | 0 | else |
152 | 0 | aPair.B() = m_aDisplayText.getLength()-1; |
153 | 0 | } |
154 | 0 | else if( nLine == 0 && nDisplayLines == 0 && !m_aDisplayText.isEmpty() ) |
155 | 0 | { |
156 | | // special case for single line controls so the implementations |
157 | | // in that case do not have to fill in the line indices |
158 | 0 | aPair.A() = 0; |
159 | 0 | aPair.B() = m_aDisplayText.getLength()-1; |
160 | 0 | } |
161 | 0 | return aPair; |
162 | 0 | } |
163 | | |
164 | | Pair Control::GetLineStartEnd( tools::Long nLine ) const |
165 | 0 | { |
166 | 0 | if( !HasLayoutData() ) |
167 | 0 | FillLayoutData(); |
168 | 0 | return mxLayoutData ? mxLayoutData->GetLineStartEnd( nLine ) : Pair( -1, -1 ); |
169 | 0 | } |
170 | | |
171 | | tools::Long ControlLayoutData::ToRelativeLineIndex( tools::Long nIndex ) const |
172 | 0 | { |
173 | | // is the index sensible at all ? |
174 | 0 | if( nIndex >= 0 && nIndex < m_aDisplayText.getLength() ) |
175 | 0 | { |
176 | 0 | int nDisplayLines = m_aLineIndices.size(); |
177 | | // if only 1 line exists, then absolute and relative index are |
178 | | // identical -> do nothing |
179 | 0 | if( nDisplayLines > 1 ) |
180 | 0 | { |
181 | 0 | int nLine; |
182 | 0 | for( nLine = nDisplayLines-1; nLine >= 0; nLine-- ) |
183 | 0 | { |
184 | 0 | if( m_aLineIndices[nLine] <= nIndex ) |
185 | 0 | { |
186 | 0 | nIndex -= m_aLineIndices[nLine]; |
187 | 0 | break; |
188 | 0 | } |
189 | 0 | } |
190 | 0 | if( nLine < 0 ) |
191 | 0 | { |
192 | 0 | SAL_WARN_IF( nLine < 0, "vcl", "ToRelativeLineIndex failed" ); |
193 | 0 | nIndex = -1; |
194 | 0 | } |
195 | 0 | } |
196 | 0 | } |
197 | 0 | else |
198 | 0 | nIndex = -1; |
199 | | |
200 | 0 | return nIndex; |
201 | 0 | } |
202 | | |
203 | | tools::Long Control::ToRelativeLineIndex( tools::Long nIndex ) const |
204 | 0 | { |
205 | 0 | if( !HasLayoutData() ) |
206 | 0 | FillLayoutData(); |
207 | 0 | return mxLayoutData ? mxLayoutData->ToRelativeLineIndex( nIndex ) : -1; |
208 | 0 | } |
209 | | |
210 | | OUString Control::GetDisplayText() const |
211 | 0 | { |
212 | 0 | if( !HasLayoutData() ) |
213 | 0 | FillLayoutData(); |
214 | 0 | return mxLayoutData ? mxLayoutData->m_aDisplayText : GetText(); |
215 | 0 | } |
216 | | |
217 | | bool Control::FocusWindowBelongsToControl(const vcl::Window* pFocusWin) const |
218 | 0 | { |
219 | 0 | return ImplIsWindowOrChild(pFocusWin); |
220 | 0 | } |
221 | | |
222 | | bool Control::EventNotify( NotifyEvent& rNEvt ) |
223 | 0 | { |
224 | 0 | if ( rNEvt.GetType() == NotifyEventType::GETFOCUS ) |
225 | 0 | { |
226 | 0 | if ( !mbHasControlFocus ) |
227 | 0 | { |
228 | 0 | mbHasControlFocus = true; |
229 | 0 | CompatStateChanged( StateChangedType::ControlFocus ); |
230 | 0 | if ( ImplCallEventListenersAndHandler( VclEventId::ControlGetFocus, {} ) ) |
231 | | // been destroyed within the handler |
232 | 0 | return true; |
233 | 0 | } |
234 | 0 | } |
235 | 0 | else |
236 | 0 | { |
237 | 0 | if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS ) |
238 | 0 | { |
239 | 0 | vcl::Window* pFocusWin = Application::GetFocusWindow(); |
240 | 0 | if ( !pFocusWin || !FocusWindowBelongsToControl(pFocusWin) ) |
241 | 0 | { |
242 | 0 | mbHasControlFocus = false; |
243 | 0 | CompatStateChanged( StateChangedType::ControlFocus ); |
244 | 0 | if ( ImplCallEventListenersAndHandler( VclEventId::ControlLoseFocus, [this] () { maLoseFocusHdl.Call(*this); } ) ) |
245 | | // been destroyed within the handler |
246 | 0 | return true; |
247 | 0 | } |
248 | 0 | } |
249 | 0 | } |
250 | 0 | return Window::EventNotify( rNEvt ); |
251 | 0 | } |
252 | | |
253 | | void Control::StateChanged( StateChangedType nStateChange ) |
254 | 0 | { |
255 | 0 | if( nStateChange == StateChangedType::InitShow || |
256 | 0 | nStateChange == StateChangedType::Visible || |
257 | 0 | nStateChange == StateChangedType::Zoom || |
258 | 0 | nStateChange == StateChangedType::ControlFont |
259 | 0 | ) |
260 | 0 | { |
261 | 0 | ImplClearLayoutData(); |
262 | 0 | } |
263 | 0 | Window::StateChanged( nStateChange ); |
264 | 0 | } |
265 | | |
266 | | void Control::AppendLayoutData( const Control& rSubControl ) const |
267 | 0 | { |
268 | 0 | if( !rSubControl.HasLayoutData() ) |
269 | 0 | rSubControl.FillLayoutData(); |
270 | 0 | if( !rSubControl.HasLayoutData() || rSubControl.mxLayoutData->m_aDisplayText.isEmpty() ) |
271 | 0 | return; |
272 | | |
273 | 0 | tools::Long nCurrentIndex = mxLayoutData->m_aDisplayText.getLength(); |
274 | 0 | mxLayoutData->m_aDisplayText += rSubControl.mxLayoutData->m_aDisplayText; |
275 | 0 | int nLines = rSubControl.mxLayoutData->m_aLineIndices.size(); |
276 | 0 | int n; |
277 | 0 | mxLayoutData->m_aLineIndices.push_back( nCurrentIndex ); |
278 | 0 | for( n = 1; n < nLines; n++ ) |
279 | 0 | mxLayoutData->m_aLineIndices.push_back( rSubControl.mxLayoutData->m_aLineIndices[n] + nCurrentIndex ); |
280 | 0 | int nRectangles = rSubControl.mxLayoutData->m_aUnicodeBoundRects.size(); |
281 | 0 | tools::Rectangle aRel = rSubControl.GetWindowExtentsRelative(*this); |
282 | 0 | for( n = 0; n < nRectangles; n++ ) |
283 | 0 | { |
284 | 0 | tools::Rectangle aRect = rSubControl.mxLayoutData->m_aUnicodeBoundRects[n]; |
285 | 0 | aRect.Move( aRel.Left(), aRel.Top() ); |
286 | 0 | mxLayoutData->m_aUnicodeBoundRects.push_back( aRect ); |
287 | 0 | } |
288 | 0 | } |
289 | | |
290 | | void Control::CallEventListeners( VclEventId nEvent, void* pData) |
291 | 0 | { |
292 | 0 | VclPtr<Control> xThis(this); |
293 | 0 | UITestLogger::getInstance().logAction(xThis, nEvent); |
294 | |
|
295 | 0 | vcl::Window::CallEventListeners(nEvent, pData); |
296 | 0 | } |
297 | | |
298 | | bool Control::ImplCallEventListenersAndHandler( VclEventId nEvent, std::function<void()> const & callHandler ) |
299 | 0 | { |
300 | 0 | VclPtr<Control> xThis(this); |
301 | |
|
302 | 0 | Control::CallEventListeners( nEvent ); |
303 | |
|
304 | 0 | if ( !xThis->isDisposed() ) |
305 | 0 | { |
306 | 0 | if (callHandler) |
307 | 0 | { |
308 | 0 | callHandler(); |
309 | 0 | } |
310 | |
|
311 | 0 | if ( !xThis->isDisposed() ) |
312 | 0 | return false; |
313 | 0 | } |
314 | 0 | return true; |
315 | 0 | } |
316 | | |
317 | | void Control::SetLayoutDataParent( const Control* pParent ) const |
318 | 0 | { |
319 | 0 | if( HasLayoutData() ) |
320 | 0 | mxLayoutData->m_pParent = pParent; |
321 | 0 | } |
322 | | |
323 | | void Control::ImplClearLayoutData() const |
324 | 0 | { |
325 | 0 | mxLayoutData.reset(); |
326 | 0 | } |
327 | | |
328 | | void Control::ImplDrawFrame( OutputDevice* pDev, tools::Rectangle& rRect ) |
329 | 0 | { |
330 | | // use a deco view to draw the frame |
331 | | // However, since there happens a lot of magic there, we need to fake some (style) settings |
332 | | // on the device |
333 | 0 | AllSettings aOriginalSettings( pDev->GetSettings() ); |
334 | |
|
335 | 0 | AllSettings aNewSettings( aOriginalSettings ); |
336 | 0 | StyleSettings aStyle( aNewSettings.GetStyleSettings() ); |
337 | | |
338 | | // The *only known* clients of the Draw methods of the various VCL-controls are form controls: |
339 | | // During print preview, and during printing, Draw is called. Thus, drawing always happens with a |
340 | | // mono (colored) border |
341 | 0 | aStyle.SetOptions( aStyle.GetOptions() | StyleSettingsOptions::Mono ); |
342 | 0 | aStyle.SetMonoColor( GetSettings().GetStyleSettings().GetMonoColor() ); |
343 | |
|
344 | 0 | aNewSettings.SetStyleSettings( aStyle ); |
345 | | // #i67023# do not call data changed listeners for this fake |
346 | | // since they may understandably invalidate on settings changed |
347 | 0 | pDev->OutputDevice::SetSettings( aNewSettings ); |
348 | |
|
349 | 0 | DecorationView aDecoView( pDev ); |
350 | 0 | rRect = aDecoView.DrawFrame( rRect, DrawFrameStyle::Out, DrawFrameFlags::WindowBorder ); |
351 | |
|
352 | 0 | pDev->OutputDevice::SetSettings( aOriginalSettings ); |
353 | 0 | } |
354 | | |
355 | | void Control::SetShowAccelerator(bool bVal) |
356 | 0 | { |
357 | 0 | mbShowAccelerator = bVal; |
358 | 0 | }; |
359 | | |
360 | | ControlLayoutData::~ControlLayoutData() |
361 | 0 | { |
362 | 0 | if( m_pParent ) |
363 | 0 | m_pParent->ImplClearLayoutData(); |
364 | 0 | } |
365 | | |
366 | | Size Control::GetOptimalSize() const |
367 | 0 | { |
368 | 0 | return Size( GetTextWidth( GetText() ) + 2 * 12, |
369 | 0 | GetTextHeight() + 2 * 6 ); |
370 | 0 | } |
371 | | |
372 | | void Control::SetReferenceDevice( OutputDevice* _referenceDevice ) |
373 | 0 | { |
374 | 0 | if ( mpReferenceDevice == _referenceDevice ) |
375 | 0 | return; |
376 | | |
377 | 0 | mpReferenceDevice = _referenceDevice; |
378 | 0 | Invalidate(); |
379 | 0 | } |
380 | | |
381 | | OutputDevice* Control::GetReferenceDevice() const |
382 | 0 | { |
383 | | // tdf#118377 It can happen that mpReferenceDevice is already disposed and |
384 | | // stays disposed (see task, even when Dialog is closed). I have no idea if |
385 | | // this may be very bad - someone who knows more about lifetime of OutputDevice's |
386 | | // will have to decide. |
387 | | // To secure this, I changed all accesses to mpControlData->mpReferenceDevice to |
388 | | // use Control::GetReferenceDevice() - only use mpControlData->mpReferenceDevice |
389 | | // inside Control::SetReferenceDevice and Control::GetReferenceDevice(). |
390 | | // Control::GetReferenceDevice() will now reset mpReferenceDevice if it is already |
391 | | // disposed. This way all usages will do a kind of 'test-and-get' call. |
392 | 0 | if(nullptr != mpReferenceDevice && mpReferenceDevice->isDisposed()) |
393 | 0 | { |
394 | 0 | const_cast<Control*>(this)->SetReferenceDevice(nullptr); |
395 | 0 | } |
396 | |
|
397 | 0 | return mpReferenceDevice; |
398 | 0 | } |
399 | | |
400 | | const vcl::Font& Control::GetCanonicalFont( const StyleSettings& _rStyle ) const |
401 | 0 | { |
402 | 0 | return _rStyle.GetLabelFont(); |
403 | 0 | } |
404 | | |
405 | | const Color& Control::GetCanonicalTextColor( const StyleSettings& _rStyle ) const |
406 | 0 | { |
407 | 0 | return _rStyle.GetLabelTextColor(); |
408 | 0 | } |
409 | | |
410 | | void Control::ApplySettings(vcl::RenderContext& rRenderContext) |
411 | 0 | { |
412 | 0 | const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); |
413 | |
|
414 | 0 | ApplyControlFont(rRenderContext, GetCanonicalFont(rStyleSettings)); |
415 | |
|
416 | 0 | ApplyControlForeground(rRenderContext, GetCanonicalTextColor(rStyleSettings)); |
417 | 0 | rRenderContext.SetTextFillColor(); |
418 | 0 | } |
419 | | |
420 | | void Control::ImplInitSettings() |
421 | 0 | { |
422 | 0 | ApplySettings(*GetOutDev()); |
423 | 0 | } |
424 | | |
425 | | tools::Rectangle Control::DrawControlText( OutputDevice& _rTargetDevice, const tools::Rectangle& rRect, const OUString& _rStr, |
426 | | DrawTextFlags _nStyle, std::vector< tools::Rectangle >* _pVector, OUString* _pDisplayText, const Size* i_pDeviceSize ) const |
427 | 0 | { |
428 | 0 | OUString rPStr = _rStr; |
429 | 0 | DrawTextFlags nPStyle = _nStyle; |
430 | |
|
431 | 0 | bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel; |
432 | |
|
433 | 0 | if (autoacc && !mbShowAccelerator) |
434 | 0 | rPStr = removeMnemonicFromString( _rStr ); |
435 | |
|
436 | 0 | if( !GetReferenceDevice() || ( GetReferenceDevice() == &_rTargetDevice ) ) |
437 | 0 | { |
438 | 0 | const tools::Rectangle aRet = _rTargetDevice.GetTextRect(rRect, rPStr, nPStyle); |
439 | 0 | _rTargetDevice.DrawText(aRet, rPStr, nPStyle, _pVector, _pDisplayText); |
440 | 0 | return aRet; |
441 | 0 | } |
442 | | |
443 | 0 | ControlTextRenderer aRenderer( *this, _rTargetDevice, *GetReferenceDevice() ); |
444 | 0 | return aRenderer.DrawText(rRect, rPStr, nPStyle, _pVector, _pDisplayText, i_pDeviceSize); |
445 | 0 | } |
446 | | |
447 | | tools::Rectangle Control::GetControlTextRect( OutputDevice& _rTargetDevice, const tools::Rectangle & rRect, |
448 | | const OUString& _rStr, DrawTextFlags _nStyle, Size* o_pDeviceSize ) const |
449 | 0 | { |
450 | 0 | OUString rPStr = _rStr; |
451 | 0 | DrawTextFlags nPStyle = _nStyle; |
452 | |
|
453 | 0 | bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel; |
454 | |
|
455 | 0 | if (autoacc && !mbShowAccelerator) |
456 | 0 | rPStr = removeMnemonicFromString( _rStr ); |
457 | |
|
458 | 0 | if ( !GetReferenceDevice() || ( GetReferenceDevice() == &_rTargetDevice ) ) |
459 | 0 | { |
460 | 0 | tools::Rectangle aRet = _rTargetDevice.GetTextRect( rRect, rPStr, nPStyle ); |
461 | 0 | if (o_pDeviceSize) |
462 | 0 | { |
463 | 0 | *o_pDeviceSize = aRet.GetSize(); |
464 | 0 | } |
465 | 0 | return aRet; |
466 | 0 | } |
467 | | |
468 | 0 | ControlTextRenderer aRenderer( *this, _rTargetDevice, *GetReferenceDevice() ); |
469 | 0 | return aRenderer.GetTextRect(rRect, rPStr, nPStyle, o_pDeviceSize); |
470 | 0 | } |
471 | | |
472 | | Font |
473 | | Control::GetUnzoomedControlPointFont() const |
474 | 0 | { |
475 | 0 | Font aFont(GetCanonicalFont(GetSettings().GetStyleSettings())); |
476 | 0 | if (IsControlFont()) |
477 | 0 | aFont.Merge(GetControlFont()); |
478 | 0 | return aFont; |
479 | 0 | } |
480 | | |
481 | | void Control::LogicInvalidate(const tools::Rectangle* pRectangle) |
482 | 0 | { |
483 | 0 | VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier(); |
484 | 0 | if (!pParent || !dynamic_cast<vcl::DocWindow*>(GetParent())) |
485 | 0 | { |
486 | | // if control doesn't belong to a DocWindow, the overridden base class |
487 | | // method has to be invoked |
488 | 0 | Window::LogicInvalidate(pRectangle); |
489 | 0 | return; |
490 | 0 | } |
491 | | |
492 | | // avoid endless paint/invalidate loop in Impress |
493 | 0 | if (comphelper::LibreOfficeKit::isTiledPainting()) |
494 | 0 | return; |
495 | | |
496 | 0 | tools::Rectangle aResultRectangle; |
497 | 0 | if (!pRectangle) |
498 | 0 | { |
499 | | // we have to invalidate the whole control area not the whole document |
500 | 0 | aResultRectangle = PixelToLogic(tools::Rectangle(GetPosPixel(), GetSizePixel()), MapMode(MapUnit::MapTwip)); |
501 | 0 | } |
502 | 0 | else |
503 | 0 | { |
504 | 0 | aResultRectangle = OutputDevice::LogicToLogic(*pRectangle, GetMapMode(), MapMode(MapUnit::MapTwip)); |
505 | 0 | } |
506 | |
|
507 | 0 | pParent->GetLOKNotifier()->notifyInvalidation(&aResultRectangle); |
508 | 0 | } |
509 | | |
510 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |