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 
46 namespace
47 {
48 const char* pDisableCursorIndicator(getenv("SAL_DISABLE_CURSOR_INDICATOR"));
49 bool 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.getHeight() * 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 
126 static 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 
198 void 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 
244 void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction )
245 {
246  VclPtr<vcl::Window> pParent = pWindow->GetParentWithLOKNotifier();
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 
289 bool 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 
318 void 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 
345 IMPL_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 
363 vcl::Cursor::Cursor( const Cursor& rCursor ) :
364  maSize( rCursor.maSize ),
365  maPos( rCursor.maPos )
366 {
367  mpData = nullptr;
368  mpWindow = nullptr;
369  mnOrientation = rCursor.mnOrientation;
370  mnDirection = rCursor.mnDirection;
371  mnStyle = 0;
372  mbVisible = rCursor.mbVisible;
373 }
374 
376 {
377  if (mpData && mpData->mbCurVisible)
378  ImplRestore();
379 }
380 
381 void 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 
417 void vcl::Cursor::SetPos( const Point& rPoint )
418 {
419  if ( maPos != rPoint )
420  {
421  maPos = rPoint;
422  ImplNew();
423  }
424 }
425 
426 void 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 
444 void vcl::Cursor::SetOrientation( Degree10 nNewOrientation )
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 
474 bool 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: */
tools::Long GetOutOffYPixel() const
Definition: window3.cxx:124
vcl::LOKWindowId GetLOKWindowId() const
Definition: window.cxx:3259
void DrawToDevice(OutputDevice &rRenderContext)
Definition: cursor.cxx:177
CursorDirection mnDirection
Definition: cursor.cxx:40
std::unique_ptr< ImplCursorData > mpData
Definition: cursor.hxx:49
void SetOrientation(Degree10 nOrientation=0_deg10)
Definition: cursor.cxx:444
sal_uInt16 mnStyle
Definition: cursor.hxx:54
bool mbCurVisible
Definition: cursor.cxx:42
void SetPos(const Point &rNewPos)
Definition: cursor.cxx:417
void setWidth(tools::Long nWidth)
SAL_DLLPRIVATE bool ImplSuspend()
Definition: cursor.cxx:323
Degree10 mnOrientation
Definition: cursor.hxx:53
long Long
Point maPos
Definition: cursor.hxx:52
const StyleSettings & GetStyleSettings() const
bool IsMapModeEnabled() const
Definition: outdev.hxx:1566
SAL_DLLPRIVATE WindowImpl * ImplGetWindowImpl() const
Definition: window.hxx:527
AutoTimer maTimer vcl ImplCursorData maTimer
Definition: cursor.cxx:35
SAL_DLLPRIVATE void ImplNew()
Definition: cursor.cxx:328
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:641
sal_uInt16 mnStyle
Definition: cursor.cxx:41
void LOKNotify(vcl::Window *pWindow, const OUString &rAction)
Definition: cursor.cxx:244
bool IsRTLEnabled() const
Definition: window3.cxx:127
tools::Long getHeight() const
constexpr tools::Long Width() const
CursorDirection mnDirection
Definition: cursor.hxx:56
Point maPixRotOff
Definition: cursor.cxx:37
void Invert(const tools::Rectangle &rRect, InvertFlags nFlags=InvertFlags::NONE)
Definition: rect.cxx:149
SAL_DLLPRIVATE void ReMirror(Point &rPoint) const
Definition: outdev.cxx:687
Point maPixPos
Definition: cursor.cxx:36
VclPtr< vcl::Window > GetParentWithLOKNotifier()
Find the nearest parent with LOK Notifier; can be itself if this Window has LOK notifier set...
Definition: window.cxx:3264
#define CURSOR_SHADOW
Definition: cursor.hxx:36
void SetStyle(sal_uInt16 nStyle)
Definition: cursor.cxx:381
#define STYLE_CURSOR_NOBLINKTIME
Definition: settings.hxx:206
void Hide()
Definition: cursor.cxx:399
Degree10 mnOrientation
Definition: cursor.cxx:39
void Rotate(const Point &rCenter, double fSin, double fCos)
constexpr OUStringLiteral aData
SAL_DLLPRIVATE void ImplResume(bool bRestore=false)
Definition: cursor.cxx:318
SAL_DLLPRIVATE void ImplDraw()
Definition: cursor.cxx:163
An auto-timer is a multi-shot timer re-emitting itself at interval until destroyed or stopped...
Definition: timer.hxx:66
VclPtr< vcl::Window > mpWindow
Point LogicToPixel(const Point &rLogicPt) const
Definition: window3.cxx:131
bool ImplDoHide(bool bStop)
Definition: cursor.cxx:289
SAL_DLLPRIVATE void ImplHide()
Definition: cursor.cxx:313
bool operator==(const Cursor &rCursor) const
Definition: cursor.cxx:474
Size maSize
Definition: cursor.hxx:51
SAL_DLLPRIVATE bool ImplIsAntiparallel() const
Definition: outdev.cxx:671
CursorDirection
Definition: cursor.hxx:38
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:175
const AllSettings & GetSettings() const
Definition: window3.cxx:129
void SetWindow(vcl::Window *pWindow)
Definition: cursor.cxx:408
bool mbVisible
Definition: cursor.hxx:55
std::unique_ptr< WindowImpl > mpWindowImpl
Definition: window.hxx:483
SAL_DLLPRIVATE void ImplShow()
Definition: cursor.cxx:308
void Show()
Definition: cursor.cxx:390
sal_uInt16 GetSize() const
void SetSize(const Size &rNewSize)
Definition: cursor.cxx:426
const AllSettings & GetSettings() const
Definition: outdev.hxx:295
Sets up the buffer to have settings matching the window, and restores the original state in the dtor...
Definition: window.h:403
VclPtr< vcl::Window > mpWindow
Definition: cursor.cxx:43
VclPtr< vcl::Window > mpWindow
Definition: cursor.hxx:50
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1164
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:933
const Point & GetPoint(sal_uInt16 nPos) const
void ImplRestore()
Definition: cursor.cxx:190
static vcl::Window * GetFocusWindow()
Get the currently focused window.
Definition: svapp.cxx:1159
std::unique_ptr< PaintBufferGuard, o3tl::default_delete< PaintBufferGuard > > PaintBufferGuardPtr
Definition: window.h:421
bool mbVisible
sal_uInt64 GetCursorBlinkTime() const
sal_Int32 GetCursorSize() const
::OutputDevice const * GetOutDev() const
Definition: window.cxx:568
Size maPixSize
Definition: cursor.cxx:38
Cursor & operator=(const Cursor &rCursor)
Definition: cursor.cxx:462
reference_type * get() const
Get the body.
Definition: vclptr.hxx:143
const vcl::ILibreOfficeKitNotifier * GetLOKNotifier() const
Definition: window.cxx:3254
bool ImplPrepForDraw(const OutputDevice *pDevice, ImplCursorData &rData)
Definition: cursor.cxx:143
Definition: timer.hxx:26
tools::Rectangle GetBoundRect() const
static tools::Rectangle ImplCursorInvert(vcl::RenderContext *pRenderContext, ImplCursorData const *pData)
Definition: cursor.cxx:52
void ImplDoShow(bool bDrawDirect, bool bRestore)
Definition: cursor.cxx:198
InvertFlags
Definition: InvertFlags.hxx:24
tools::Long GetOutOffXPixel() const
Definition: window3.cxx:123
rtl::OString toString() const
IMPL_LINK_NOARG(QuickSelectionEngine_Data, SearchStringTimeout, Timer *, void)
ImplFrameData * mpFrameData
Definition: window.h:228
void SetDirection(CursorDirection nDirection=CursorDirection::NONE)
Definition: cursor.cxx:453
bool SupportsDoubleBuffering() const
Can the widget derived from this Window do the double-buffering via RenderContext properly...
Definition: window.cxx:3860
bool isDisposed() const
void SetWidth(tools::Long nNewWidth)
Definition: cursor.cxx:435
::basegfx::B2IVector maSize