LibreOffice Module vcl (master) 1
cursor.cxx
Go to the documentation of this file.
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 <memory>
21
22#include <comphelper/lok.hxx>
23#include <vcl/svapp.hxx>
24#include <vcl/timer.hxx>
25#include <vcl/settings.hxx>
26#include <vcl/window.hxx>
27#include <vcl/cursor.hxx>
28
29#include <window.h>
30
31#include <tools/poly.hxx>
32
34{
35 AutoTimer maTimer { "vcl ImplCursorData maTimer" }; // Timer
36 Point maPixPos; // Pixel-Position
37 Point maPixRotOff; // Pixel-Offset-Position
38 Size maPixSize; // Pixel-Size
39 Degree10 mnOrientation; // Pixel-Orientation
40 CursorDirection mnDirection; // indicates writing direction
41 sal_uInt16 mnStyle; // Cursor-Style
42 bool mbCurVisible; // Is cursor currently visible
43 VclPtr<vcl::Window> mpWindow; // assigned window
44};
45
46namespace
47{
48const char* pDisableCursorIndicator(getenv("SAL_DISABLE_CURSOR_INDICATOR"));
49bool bDisableCursorIndicator(nullptr != pDisableCursorIndicator);
50}
51
53{
54 tools::Rectangle aPaintRect;
55
56 bool bMapMode = pRenderContext->IsMapModeEnabled();
57 pRenderContext->EnableMapMode( false );
58 InvertFlags nInvertStyle;
59 if ( pData->mnStyle & CURSOR_SHADOW )
60 nInvertStyle = InvertFlags::N50;
61 else
62 nInvertStyle = InvertFlags::NONE;
63
64 tools::Rectangle aRect( pData->maPixPos, pData->maPixSize );
65 if ( pData->mnDirection != CursorDirection::NONE || pData->mnOrientation )
66 {
67 tools::Polygon aPoly( aRect );
68 if( aPoly.GetSize() == 5 )
69 {
70 aPoly[1].AdjustX(1 ); // include the right border
71 aPoly[2].AdjustX(1 );
72
73 // apply direction flag after slant to use the correct shape
74 if (!bDisableCursorIndicator && pData->mnDirection != CursorDirection::NONE)
75 {
76 Point pAry[7];
77 // Related system settings for "delta" could be:
78 // gtk cursor-aspect-ratio and windows SPI_GETCARETWIDTH
79 int delta = (aRect.getOpenHeight() * 4 / 100) + 1;
80 if( pData->mnDirection == CursorDirection::LTR )
81 {
82 // left-to-right
83 pAry[0] = aPoly.GetPoint( 0 );
84 pAry[1] = aPoly.GetPoint( 1 );
85 pAry[2] = pAry[1];
86 pAry[2].AdjustX(delta);
87 pAry[2].AdjustY(delta);
88 pAry[3] = pAry[1];
89 pAry[3].AdjustY(delta * 2);
90 pAry[4] = aPoly.GetPoint( 2 );
91 pAry[5] = aPoly.GetPoint( 3 );
92 pAry[6] = aPoly.GetPoint( 4 );
93 }
94 else if( pData->mnDirection == CursorDirection::RTL )
95 {
96 // right-to-left
97 pAry[0] = aPoly.GetPoint( 0 );
98 pAry[1] = aPoly.GetPoint( 1 );
99 pAry[2] = aPoly.GetPoint( 2 );
100 pAry[3] = aPoly.GetPoint( 3 );
101 pAry[4] = pAry[0];
102 pAry[4].AdjustY(delta*2);
103 pAry[5] = pAry[0];
104 pAry[5].AdjustX(-delta);
105 pAry[5].AdjustY(delta);
106 pAry[6] = aPoly.GetPoint( 4 );
107 }
108 aPoly = tools::Polygon( 7, pAry);
109 }
110
111 if ( pData->mnOrientation )
112 aPoly.Rotate( pData->maPixRotOff, pData->mnOrientation );
113 pRenderContext->Invert( aPoly, nInvertStyle );
114 aPaintRect = aPoly.GetBoundRect();
115 }
116 }
117 else
118 {
119 pRenderContext->Invert( aRect, nInvertStyle );
120 aPaintRect = aRect;
121 }
122 pRenderContext->EnableMapMode( bMapMode );
123 return aPaintRect;
124}
125
126static void ImplCursorInvert(vcl::Window* pWindow, ImplCursorData const * pData)
127{
128 if (!pWindow || pWindow->isDisposed())
129 return;
130
132 const bool bDoubleBuffering = pWindow->SupportsDoubleBuffering();
133 if (bDoubleBuffering)
134 pGuard.reset(new vcl::PaintBufferGuard(pWindow->ImplGetWindowImpl()->mpFrameData, pWindow));
135
136 vcl::RenderContext* pRenderContext = bDoubleBuffering ? pGuard->GetRenderContext() : pWindow->GetOutDev();
137
138 tools::Rectangle aPaintRect = ImplCursorInvert(pRenderContext, pData);
139 if (bDoubleBuffering)
140 pGuard->SetPaintRect(pRenderContext->PixelToLogic(aPaintRect));
141}
142
144{
145 if (pDevice && !rData.mbCurVisible)
146 {
147 rData.maPixPos = pDevice->LogicToPixel( maPos );
148 rData.maPixSize = pDevice->LogicToPixel( maSize );
150 rData.mnDirection = mnDirection;
151
152 // correct the position with the offset
153 rData.maPixRotOff = rData.maPixPos;
154
155 // use width (as set in Settings) if size is 0,
156 if (!rData.maPixSize.Width())
158 return true;
159 }
160 return false;
161}
162
164{
165 if (mpData && mpData->mpWindow)
166 {
167 // calculate output area
168 if (ImplPrepForDraw(mpData->mpWindow->GetOutDev(), *mpData))
169 {
170 // display
171 ImplCursorInvert(mpData->mpWindow, mpData.get());
172 mpData->mbCurVisible = true;
173 }
174 }
175}
176
178{
180 aData.mnStyle = 0;
181 aData.mbCurVisible = false;
182 // calculate output area
183 if (ImplPrepForDraw(&rRenderContext, aData))
184 {
185 // display
186 ImplCursorInvert(&rRenderContext, &aData);
187 }
188}
189
191{
192 assert( mpData && mpData->mbCurVisible );
193
194 ImplCursorInvert(mpData->mpWindow, mpData.get());
195 mpData->mbCurVisible = false;
196}
197
198void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool bRestore )
199{
200 if ( !mbVisible )
201 return;
202
203 vcl::Window* pWindow;
204 if ( mpWindow )
205 pWindow = mpWindow;
206 else
207 {
208 // show the cursor, if there is an active window and the cursor
209 // has been selected in this window
210 pWindow = Application::GetFocusWindow();
211 if (!pWindow || !pWindow->mpWindowImpl || (pWindow->mpWindowImpl->mpCursor != this)
212 || pWindow->mpWindowImpl->mbInPaint
213 || !pWindow->mpWindowImpl->mpFrameData->mbHasFocus)
214 pWindow = nullptr;
215 }
216
217 if ( !pWindow )
218 return;
219
220 if ( !mpData )
221 {
222 mpData.reset( new ImplCursorData );
223 mpData->mbCurVisible = false;
224 mpData->maTimer.SetInvokeHandler( LINK( this, Cursor, ImplTimerHdl ) );
225 }
226
227 mpData->mpWindow = pWindow;
228 mpData->mnStyle = mnStyle;
229 if ( bDrawDirect || bRestore )
230 ImplDraw();
231
232 if ( !mpWindow && (bDrawDirect || !mpData->maTimer.IsActive()) )
233 {
234 mpData->maTimer.SetTimeout( pWindow->GetSettings().GetStyleSettings().GetCursorBlinkTime() );
235 if ( mpData->maTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME )
236 mpData->maTimer.Start();
237 else if ( !mpData->mbCurVisible )
238 ImplDraw();
239 LOKNotify( pWindow, "cursor_invalidate" );
240 LOKNotify( pWindow, "cursor_visible" );
241 }
242}
243
244void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction )
245{
247 if (!pParent)
248 return;
249
250 assert(pWindow && "Cannot notify without a window");
251 assert(mpData && "Require ImplCursorData");
253
255 return;
256
257 const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
258 std::vector<vcl::LOKPayloadItem> aItems;
259 if (rAction == "cursor_visible")
260 aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : "false");
261 else if (rAction == "cursor_invalidate")
262 {
263 const tools::Long nX = pWindow->GetOutOffXPixel() + pWindow->LogicToPixel(GetPos()).X() - pParent->GetOutOffXPixel();
264 const tools::Long nY = pWindow->GetOutOffYPixel() + pWindow->LogicToPixel(GetPos()).Y() - pParent->GetOutOffYPixel();
265 Size aSize = pWindow->LogicToPixel(GetSize());
266 if (!aSize.Width())
267 aSize.setWidth( pWindow->GetSettings().GetStyleSettings().GetCursorSize() );
268
269 Point aPos(nX, nY);
270
271 if (pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev()
272 && !pWindow->GetOutDev()->ImplIsAntiparallel())
273 pParent->GetOutDev()->ReMirror(aPos);
274
275 if (!pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev()
276 && pWindow->GetOutDev()->ImplIsAntiparallel())
277 {
278 pWindow->GetOutDev()->ReMirror(aPos);
279 pParent->GetOutDev()->ReMirror(aPos);
280 }
281
282 const tools::Rectangle aRect(aPos, aSize);
283 aItems.emplace_back("rectangle", aRect.toString());
284 }
285
286 pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems);
287}
288
289bool vcl::Cursor::ImplDoHide( bool bSuspend )
290{
291 bool bWasCurVisible = false;
292 if ( mpData && mpData->mpWindow )
293 {
294 bWasCurVisible = mpData->mbCurVisible;
295 if ( mpData->mbCurVisible )
296 ImplRestore();
297
298 if ( !bSuspend )
299 {
300 LOKNotify( mpData->mpWindow, "cursor_visible" );
301 mpData->maTimer.Stop();
302 mpData->mpWindow = nullptr;
303 }
304 }
305 return bWasCurVisible;
306}
307
309{
310 ImplDoShow( true/*bDrawDirect*/, false );
311}
312
314{
315 ImplDoHide( false );
316}
317
318void vcl::Cursor::ImplResume( bool bRestore )
319{
320 ImplDoShow( false, bRestore );
321}
322
324{
325 return ImplDoHide( true );
326}
327
329{
330 if ( !(mbVisible && mpData && mpData->mpWindow) )
331 return;
332
333 if ( mpData->mbCurVisible )
334 ImplRestore();
335
336 ImplDraw();
337 if ( !mpWindow )
338 {
339 LOKNotify( mpData->mpWindow, "cursor_invalidate" );
340 if ( mpData->maTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME )
341 mpData->maTimer.Start();
342 }
343}
344
345IMPL_LINK_NOARG(vcl::Cursor, ImplTimerHdl, Timer *, void)
346{
347 if ( mpData->mbCurVisible )
348 ImplRestore();
349 else
350 ImplDraw();
351}
352
354{
355 mpData = nullptr;
356 mpWindow = nullptr;
357 mnOrientation = 0_deg10;
358 mnDirection = CursorDirection::NONE;
359 mnStyle = 0;
360 mbVisible = false;
361}
362
363vcl::Cursor::Cursor( const Cursor& rCursor ) :
364 maSize( rCursor.maSize ),
365 maPos( rCursor.maPos )
366{
367 mpData = nullptr;
368 mpWindow = nullptr;
370 mnDirection = rCursor.mnDirection;
371 mnStyle = 0;
372 mbVisible = rCursor.mbVisible;
373}
374
376{
377 if (mpData && mpData->mbCurVisible)
378 ImplRestore();
379}
380
381void vcl::Cursor::SetStyle( sal_uInt16 nStyle )
382{
383 if ( mnStyle != nStyle )
384 {
385 mnStyle = nStyle;
386 ImplNew();
387 }
388}
389
391{
392 if ( !mbVisible )
393 {
394 mbVisible = true;
395 ImplShow();
396 }
397}
398
400{
401 if ( mbVisible )
402 {
403 mbVisible = false;
404 ImplHide();
405 }
406}
407
409{
410 if ( mpWindow.get() != pWindow )
411 {
412 mpWindow = pWindow;
413 ImplNew();
414 }
415}
416
417void vcl::Cursor::SetPos( const Point& rPoint )
418{
419 if ( maPos != rPoint )
420 {
421 maPos = rPoint;
422 ImplNew();
423 }
424}
425
426void vcl::Cursor::SetSize( const Size& rSize )
427{
428 if ( maSize != rSize )
429 {
430 maSize = rSize;
431 ImplNew();
432 }
433}
434
436{
437 if ( maSize.Width() != nNewWidth )
438 {
439 maSize.setWidth( nNewWidth );
440 ImplNew();
441 }
442}
443
445{
446 if ( mnOrientation != nNewOrientation )
447 {
448 mnOrientation = nNewOrientation;
449 ImplNew();
450 }
451}
452
454{
455 if ( mnDirection != nNewDirection )
456 {
457 mnDirection = nNewDirection;
458 ImplNew();
459 }
460}
461
463{
464 maPos = rCursor.maPos;
465 maSize = rCursor.maSize;
466 mnOrientation = rCursor.mnOrientation;
467 mnDirection = rCursor.mnDirection;
468 mbVisible = rCursor.mbVisible;
469 ImplNew();
470
471 return *this;
472}
473
474bool vcl::Cursor::operator==( const vcl::Cursor& rCursor ) const
475{
476 return
477 ((maPos == rCursor.maPos) &&
478 (maSize == rCursor.maSize) &&
479 (mnOrientation == rCursor.mnOrientation) &&
480 (mnDirection == rCursor.mnDirection) &&
481 (mbVisible == rCursor.mbVisible))
482 ;
483}
484
485/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
InvertFlags
Definition: InvertFlags.hxx:25
geometry::RealSize2D maSize
const StyleSettings & GetStyleSettings() const
static vcl::Window * GetFocusWindow()
Get the currently focused window.
Definition: svapp.cxx:1038
An auto-timer is a multi-shot timer re-emitting itself at interval until destroyed or stopped.
Definition: timer.hxx:67
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:170
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:589
SAL_DLLPRIVATE bool ImplIsAntiparallel() const
Definition: outdev.cxx:655
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1110
void Invert(const tools::Rectangle &rRect, InvertFlags nFlags=InvertFlags::NONE)
Definition: rect.cxx:148
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:879
SAL_DLLPRIVATE void ReMirror(Point &rPoint) const
Definition: outdev.cxx:671
const AllSettings & GetSettings() const
Definition: outdev.hxx:288
bool IsMapModeEnabled() const
Definition: outdev.hxx:1551
constexpr tools::Long Y() const
tools::Long AdjustY(tools::Long nVertMove)
tools::Long AdjustX(tools::Long nHorzMove)
constexpr tools::Long X() const
void setWidth(tools::Long nWidth)
constexpr tools::Long Width() const
sal_uInt64 GetCursorBlinkTime() const
sal_Int32 GetCursorSize() const
Definition: timer.hxx:27
reference_type * get() const
Get the body.
Definition: vclptr.hxx:143
bool isDisposed() const
ImplFrameData * mpFrameData
Definition: window.h:228
sal_uInt16 GetSize() const
const Point & GetPoint(sal_uInt16 nPos) const
tools::Rectangle GetBoundRect() const
void Rotate(const Point &rCenter, double fSin, double fCos)
tools::Long getOpenHeight() const
rtl::OString toString() const
Degree10 mnOrientation
Definition: cursor.hxx:53
SAL_DLLPRIVATE void ImplShow()
Definition: cursor.cxx:308
bool ImplPrepForDraw(const OutputDevice *pDevice, ImplCursorData &rData)
Definition: cursor.cxx:143
SAL_DLLPRIVATE void ImplDraw()
Definition: cursor.cxx:163
Cursor & operator=(const Cursor &rCursor)
Definition: cursor.cxx:462
void ImplRestore()
Definition: cursor.cxx:190
void ImplDoShow(bool bDrawDirect, bool bRestore)
Definition: cursor.cxx:198
bool ImplDoHide(bool bStop)
Definition: cursor.cxx:289
sal_uInt16 mnStyle
Definition: cursor.hxx:54
Size maSize
Definition: cursor.hxx:51
VclPtr< vcl::Window > mpWindow
Definition: cursor.hxx:50
void SetWindow(vcl::Window *pWindow)
Definition: cursor.cxx:408
void Hide()
Definition: cursor.cxx:399
std::unique_ptr< ImplCursorData > mpData
Definition: cursor.hxx:49
SAL_DLLPRIVATE void ImplResume(bool bRestore=false)
Definition: cursor.cxx:318
void SetStyle(sal_uInt16 nStyle)
Definition: cursor.cxx:381
bool mbVisible
Definition: cursor.hxx:55
void SetPos(const Point &rNewPos)
Definition: cursor.cxx:417
CursorDirection mnDirection
Definition: cursor.hxx:56
SAL_DLLPRIVATE void ImplHide()
Definition: cursor.cxx:313
void SetOrientation(Degree10 nOrientation=0_deg10)
Definition: cursor.cxx:444
SAL_DLLPRIVATE bool ImplSuspend()
Definition: cursor.cxx:323
bool operator==(const Cursor &rCursor) const
Definition: cursor.cxx:474
SAL_DLLPRIVATE void ImplNew()
Definition: cursor.cxx:328
void Show()
Definition: cursor.cxx:390
Point maPos
Definition: cursor.hxx:52
void DrawToDevice(OutputDevice &rRenderContext)
Definition: cursor.cxx:177
void SetWidth(tools::Long nNewWidth)
Definition: cursor.cxx:435
void SetSize(const Size &rNewSize)
Definition: cursor.cxx:426
void SetDirection(CursorDirection nDirection=CursorDirection::NONE)
Definition: cursor.cxx:453
void LOKNotify(vcl::Window *pWindow, const OUString &rAction)
Definition: cursor.cxx:244
virtual void notifyWindow(vcl::LOKWindowId nLOKWindowId, const OUString &rAction, const std::vector< LOKPayloadItem > &rPayload=std::vector< LOKPayloadItem >()) const =0
Callbacks.
Sets up the buffer to have settings matching the window, and restores the original state in the dtor.
Definition: window.h:405
tools::Long GetOutOffXPixel() const
Definition: window3.cxx:123
Point LogicToPixel(const Point &rLogicPt) const
Definition: window3.cxx:131
bool SupportsDoubleBuffering() const
Can the widget derived from this Window do the double-buffering via RenderContext properly?
Definition: window.cxx:3860
const AllSettings & GetSettings() const
Definition: window3.cxx:129
tools::Long GetOutOffYPixel() const
Definition: window3.cxx:124
VclPtr< vcl::Window > GetParentWithLOKNotifier()
Find the nearest parent with LOK Notifier; can be itself if this Window has LOK notifier set.
Definition: window.cxx:3256
::OutputDevice const * GetOutDev() const
Definition: window.cxx:567
const vcl::ILibreOfficeKitNotifier * GetLOKNotifier() const
Definition: window.cxx:3246
SAL_DLLPRIVATE WindowImpl * ImplGetWindowImpl() const
Definition: window.hxx:528
std::unique_ptr< WindowImpl > mpWindowImpl
Definition: window.hxx:484
vcl::LOKWindowId GetLOKWindowId() const
Definition: window.cxx:3251
bool IsRTLEnabled() const
Definition: window3.cxx:127
static tools::Rectangle ImplCursorInvert(vcl::RenderContext *pRenderContext, ImplCursorData const *pData)
Definition: cursor.cxx:52
CursorDirection
Definition: cursor.hxx:39
#define CURSOR_SHADOW
Definition: cursor.hxx:36
std::unique_ptr< sal_Int32[]> pData
constexpr OUStringLiteral aData
long Long
std::unique_ptr< PaintBufferGuard, o3tl::default_delete< PaintBufferGuard > > PaintBufferGuardPtr
Definition: window.h:422
IMPL_LINK_NOARG(QuickSelectionEngine_Data, SearchStringTimeout, Timer *, void)
#define STYLE_CURSOR_NOBLINKTIME
Definition: settings.hxx:206
sal_uInt16 mnStyle
Definition: cursor.cxx:41
Point maPixPos
Definition: cursor.cxx:36
Degree10 mnOrientation
Definition: cursor.cxx:39
VclPtr< vcl::Window > mpWindow
Definition: cursor.cxx:43
CursorDirection mnDirection
Definition: cursor.cxx:40
AutoTimer maTimer
Definition: cursor.cxx:35
Size maPixSize
Definition: cursor.cxx:38
Point maPixRotOff
Definition: cursor.cxx:37
bool mbCurVisible
Definition: cursor.cxx:42
bool mbVisible
VclPtr< vcl::Window > mpWindow