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 
33 struct ImplCursorData
34 {
35     AutoTimer       maTimer;            // Timer
36     Point           maPixPos;           // Pixel-Position
37     Point           maPixRotOff;        // Pixel-Offset-Position
38     Size            maPixSize;          // Pixel-Size
39     short           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 
ImplCursorInvert(vcl::RenderContext * pRenderContext,ImplCursorData const * pData)46 static tools::Rectangle ImplCursorInvert(vcl::RenderContext* pRenderContext, ImplCursorData const * pData)
47 {
48     tools::Rectangle aPaintRect;
49     bool    bMapMode = pRenderContext->IsMapModeEnabled();
50     pRenderContext->EnableMapMode( false );
51     InvertFlags nInvertStyle;
52     if ( pData->mnStyle & CURSOR_SHADOW )
53         nInvertStyle = InvertFlags::N50;
54     else
55         nInvertStyle = InvertFlags::NONE;
56 
57     tools::Rectangle aRect( pData->maPixPos, pData->maPixSize );
58     if ( pData->mnDirection != CursorDirection::NONE || pData->mnOrientation )
59     {
60         tools::Polygon aPoly( aRect );
61         if( aPoly.GetSize() == 5 )
62         {
63             aPoly[1].AdjustX(1 );  // include the right border
64             aPoly[2].AdjustX(1 );
65 
66             // apply direction flag after slant to use the correct shape
67             if ( pData->mnDirection != CursorDirection::NONE)
68             {
69                 Point pAry[7];
70                 int delta = 3*aRect.getWidth()+1;
71                 if( pData->mnDirection == CursorDirection::LTR )
72                 {
73                     // left-to-right
74                     pAry[0] = aPoly.GetPoint( 0 );
75                     pAry[1] = aPoly.GetPoint( 1 );
76                     pAry[2] = pAry[1];
77                     pAry[2].AdjustX(delta );
78                     pAry[3] =  pAry[1];
79                     pAry[3].AdjustY(delta );
80                     pAry[4] = aPoly.GetPoint( 2 );
81                     pAry[5] = aPoly.GetPoint( 3 );
82                     pAry[6] = aPoly.GetPoint( 4 );
83                 }
84                 else if( pData->mnDirection == CursorDirection::RTL )
85                 {
86                     // right-to-left
87                     pAry[0] = aPoly.GetPoint( 0 );
88                     pAry[1] = aPoly.GetPoint( 1 );
89                     pAry[2] = aPoly.GetPoint( 2 );
90                     pAry[3] = aPoly.GetPoint( 3 );
91                     pAry[4] = pAry[0];
92                     pAry[4].AdjustY(delta );
93                     pAry[5] =  pAry[0];
94                     pAry[5].AdjustX( -delta );
95                     pAry[6] = aPoly.GetPoint( 4 );
96                 }
97                 aPoly = tools::Polygon( 7, pAry);
98             }
99 
100             if ( pData->mnOrientation )
101                 aPoly.Rotate( pData->maPixRotOff, pData->mnOrientation );
102             pRenderContext->Invert( aPoly, nInvertStyle );
103             aPaintRect = aPoly.GetBoundRect();
104         }
105     }
106     else
107     {
108         pRenderContext->Invert( aRect, nInvertStyle );
109         aPaintRect = aRect;
110     }
111     pRenderContext->EnableMapMode( bMapMode );
112     return aPaintRect;
113 }
114 
ImplCursorInvert(vcl::Window * pWindow,ImplCursorData const * pData)115 static void ImplCursorInvert(vcl::Window* pWindow, ImplCursorData const * pData)
116 {
117     std::unique_ptr<vcl::PaintBufferGuard> pGuard;
118     const bool bDoubleBuffering = pWindow->SupportsDoubleBuffering();
119     if (bDoubleBuffering)
120         pGuard.reset(new vcl::PaintBufferGuard(pWindow->ImplGetWindowImpl()->mpFrameData, pWindow));
121 
122     vcl::RenderContext* pRenderContext = bDoubleBuffering ? pGuard->GetRenderContext() : pWindow;
123 
124     tools::Rectangle aPaintRect = ImplCursorInvert(pRenderContext, pData);
125     if (bDoubleBuffering)
126         pGuard->SetPaintRect(pRenderContext->PixelToLogic(aPaintRect));
127 }
128 
ImplPrepForDraw(const OutputDevice * pDevice,ImplCursorData & rData)129 bool vcl::Cursor::ImplPrepForDraw(const OutputDevice* pDevice, ImplCursorData& rData)
130 {
131     if (pDevice && !rData.mbCurVisible)
132     {
133         rData.maPixPos        = pDevice->LogicToPixel( maPos );
134         rData.maPixSize       = pDevice->LogicToPixel( maSize );
135         rData.mnOrientation   = mnOrientation;
136         rData.mnDirection     = mnDirection;
137 
138         // correct the position with the offset
139         rData.maPixRotOff = rData.maPixPos;
140 
141         // use width (as set in Settings) if size is 0,
142         if (!rData.maPixSize.Width())
143             rData.maPixSize.setWidth(pDevice->GetSettings().GetStyleSettings().GetCursorSize());
144         return true;
145     }
146     return false;
147 }
148 
ImplDraw()149 void vcl::Cursor::ImplDraw()
150 {
151     if (mpData && mpData->mpWindow)
152     {
153         // calculate output area
154         if (ImplPrepForDraw(mpData->mpWindow, *mpData))
155         {
156             // display
157             ImplCursorInvert(mpData->mpWindow, mpData.get());
158             mpData->mbCurVisible = true;
159         }
160     }
161 }
162 
DrawToDevice(OutputDevice & rRenderContext)163 void vcl::Cursor::DrawToDevice(OutputDevice& rRenderContext)
164 {
165     ImplCursorData aData;
166     aData.mnStyle = 0;
167     aData.mbCurVisible = false;
168     // calculate output area
169     if (ImplPrepForDraw(&rRenderContext, aData))
170     {
171         // display
172         ImplCursorInvert(&rRenderContext, &aData);
173     }
174 }
175 
ImplRestore()176 void vcl::Cursor::ImplRestore()
177 {
178     assert( mpData && mpData->mbCurVisible );
179 
180     ImplCursorInvert(mpData->mpWindow, mpData.get());
181     mpData->mbCurVisible = false;
182 }
183 
ImplDoShow(bool bDrawDirect,bool bRestore)184 void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool bRestore )
185 {
186     if ( mbVisible )
187     {
188         vcl::Window* pWindow;
189         if ( mpWindow )
190             pWindow = mpWindow;
191         else
192         {
193             // show the cursor, if there is an active window and the cursor
194             // has been selected in this window
195             pWindow = Application::GetFocusWindow();
196             if ( !pWindow || (pWindow->mpWindowImpl->mpCursor != this) || pWindow->mpWindowImpl->mbInPaint
197                 || !pWindow->mpWindowImpl->mpFrameData->mbHasFocus )
198                 pWindow = nullptr;
199         }
200 
201         if ( pWindow )
202         {
203             if ( !mpData )
204             {
205                 mpData.reset( new ImplCursorData );
206                 mpData->mbCurVisible = false;
207                 mpData->maTimer.SetInvokeHandler( LINK( this, Cursor, ImplTimerHdl ) );
208                 mpData->maTimer.SetDebugName( "vcl ImplCursorData maTimer" );
209             }
210 
211             mpData->mpWindow    = pWindow;
212             mpData->mnStyle     = mnStyle;
213             if ( bDrawDirect || bRestore )
214                 ImplDraw();
215 
216             if ( !mpWindow && ! ( ! bDrawDirect && mpData->maTimer.IsActive()) )
217             {
218                 mpData->maTimer.SetTimeout( pWindow->GetSettings().GetStyleSettings().GetCursorBlinkTime() );
219                 if ( mpData->maTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME )
220                     mpData->maTimer.Start();
221                 else if ( !mpData->mbCurVisible )
222                     ImplDraw();
223                 LOKNotify( pWindow, "cursor_invalidate" );
224                 LOKNotify( pWindow, "cursor_visible" );
225             }
226         }
227     }
228 }
229 
LOKNotify(vcl::Window * pWindow,const OUString & rAction)230 void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction )
231 {
232     if (VclPtr<vcl::Window> pParent = pWindow->GetParentWithLOKNotifier())
233     {
234         assert(pWindow && "Cannot notify without a window");
235         assert(mpData && "Require ImplCursorData");
236         assert(comphelper::LibreOfficeKit::isActive());
237 
238         if (comphelper::LibreOfficeKit::isDialogPainting())
239             return;
240 
241         const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
242         std::vector<vcl::LOKPayloadItem> aItems;
243         if (rAction == "cursor_visible")
244             aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : "false");
245         else if (rAction == "cursor_invalidate")
246         {
247             const long nX = pWindow->GetOutOffXPixel() + pWindow->LogicToPixel(GetPos()).X();
248             const long nY = pWindow->GetOutOffYPixel() + pWindow->LogicToPixel(GetPos()).Y();
249             Size aSize = pWindow->LogicToPixel(GetSize());
250             if (!aSize.Width())
251                 aSize.setWidth( pWindow->GetSettings().GetStyleSettings().GetCursorSize() );
252 
253             const tools::Rectangle aRect(Point(nX, nY), aSize);
254             aItems.emplace_back("rectangle", aRect.toString());
255         }
256 
257         pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems);
258     }
259 }
260 
ImplDoHide(bool bSuspend)261 bool vcl::Cursor::ImplDoHide( bool bSuspend )
262 {
263     bool bWasCurVisible = false;
264     if ( mpData && mpData->mpWindow )
265     {
266         bWasCurVisible = mpData->mbCurVisible;
267         if ( mpData->mbCurVisible )
268             ImplRestore();
269 
270         if ( !bSuspend )
271         {
272             LOKNotify( mpData->mpWindow, "cursor_visible" );
273             mpData->maTimer.Stop();
274             mpData->mpWindow = nullptr;
275         }
276     }
277     return bWasCurVisible;
278 }
279 
ImplShow()280 void vcl::Cursor::ImplShow()
281 {
282     ImplDoShow( true/*bDrawDirect*/, false );
283 }
284 
ImplHide()285 void vcl::Cursor::ImplHide()
286 {
287     ImplDoHide( false );
288 }
289 
ImplResume(bool bRestore)290 void vcl::Cursor::ImplResume( bool bRestore )
291 {
292     ImplDoShow( false, bRestore );
293 }
294 
ImplSuspend()295 bool vcl::Cursor::ImplSuspend()
296 {
297     return ImplDoHide( true );
298 }
299 
ImplNew()300 void vcl::Cursor::ImplNew()
301 {
302     if ( mbVisible && mpData && mpData->mpWindow )
303     {
304         if ( mpData->mbCurVisible )
305             ImplRestore();
306 
307         ImplDraw();
308         if ( !mpWindow )
309         {
310             LOKNotify( mpData->mpWindow, "cursor_invalidate" );
311             if ( mpData->maTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME )
312                 mpData->maTimer.Start();
313         }
314     }
315 }
316 
IMPL_LINK_NOARG(vcl::Cursor,ImplTimerHdl,Timer *,void)317 IMPL_LINK_NOARG(vcl::Cursor, ImplTimerHdl, Timer *, void)
318 {
319     if ( mpData->mbCurVisible )
320         ImplRestore();
321     else
322         ImplDraw();
323 }
324 
Cursor()325 vcl::Cursor::Cursor()
326 {
327     mpData          = nullptr;
328     mpWindow        = nullptr;
329     mnOrientation   = 0;
330     mnDirection     = CursorDirection::NONE;
331     mnStyle         = 0;
332     mbVisible       = false;
333 }
334 
Cursor(const Cursor & rCursor)335 vcl::Cursor::Cursor( const Cursor& rCursor ) :
336     maSize( rCursor.maSize ),
337     maPos( rCursor.maPos )
338 {
339     mpData          = nullptr;
340     mpWindow        = nullptr;
341     mnOrientation   = rCursor.mnOrientation;
342     mnDirection     = rCursor.mnDirection;
343     mnStyle         = 0;
344     mbVisible       = rCursor.mbVisible;
345 }
346 
~Cursor()347 vcl::Cursor::~Cursor()
348 {
349     if (mpData && mpData->mbCurVisible)
350         ImplRestore();
351 }
352 
SetStyle(sal_uInt16 nStyle)353 void vcl::Cursor::SetStyle( sal_uInt16 nStyle )
354 {
355     if ( mnStyle != nStyle )
356     {
357         mnStyle = nStyle;
358         ImplNew();
359     }
360 }
361 
Show()362 void vcl::Cursor::Show()
363 {
364     if ( !mbVisible )
365     {
366         mbVisible = true;
367         ImplShow();
368     }
369 }
370 
Hide()371 void vcl::Cursor::Hide()
372 {
373     if ( mbVisible )
374     {
375         mbVisible = false;
376         ImplHide();
377     }
378 }
379 
SetWindow(vcl::Window * pWindow)380 void vcl::Cursor::SetWindow( vcl::Window* pWindow )
381 {
382     if ( mpWindow.get() != pWindow )
383     {
384         mpWindow = pWindow;
385         ImplNew();
386     }
387 }
388 
SetPos(const Point & rPoint)389 void vcl::Cursor::SetPos( const Point& rPoint )
390 {
391     if ( maPos != rPoint )
392     {
393         maPos = rPoint;
394         ImplNew();
395     }
396 }
397 
SetSize(const Size & rSize)398 void vcl::Cursor::SetSize( const Size& rSize )
399 {
400     if ( maSize != rSize )
401     {
402         maSize = rSize;
403         ImplNew();
404     }
405 }
406 
SetWidth(long nNewWidth)407 void vcl::Cursor::SetWidth( long nNewWidth )
408 {
409     if ( maSize.Width() != nNewWidth )
410     {
411         maSize.setWidth( nNewWidth );
412         ImplNew();
413     }
414 }
415 
SetOrientation(short nNewOrientation)416 void vcl::Cursor::SetOrientation( short nNewOrientation )
417 {
418     if ( mnOrientation != nNewOrientation )
419     {
420         mnOrientation = nNewOrientation;
421         ImplNew();
422     }
423 }
424 
SetDirection(CursorDirection nNewDirection)425 void vcl::Cursor::SetDirection( CursorDirection nNewDirection )
426 {
427     if ( mnDirection != nNewDirection )
428     {
429         mnDirection = nNewDirection;
430         ImplNew();
431     }
432 }
433 
operator =(const vcl::Cursor & rCursor)434 vcl::Cursor& vcl::Cursor::operator=( const vcl::Cursor& rCursor )
435 {
436     maPos           = rCursor.maPos;
437     maSize          = rCursor.maSize;
438     mnOrientation   = rCursor.mnOrientation;
439     mnDirection     = rCursor.mnDirection;
440     mbVisible       = rCursor.mbVisible;
441     ImplNew();
442 
443     return *this;
444 }
445 
operator ==(const vcl::Cursor & rCursor) const446 bool vcl::Cursor::operator==( const vcl::Cursor& rCursor ) const
447 {
448     return
449         ((maPos         == rCursor.maPos)           &&
450          (maSize        == rCursor.maSize)          &&
451          (mnOrientation == rCursor.mnOrientation)   &&
452          (mnDirection   == rCursor.mnDirection)     &&
453          (mbVisible     == rCursor.mbVisible))
454         ;
455 }
456 
457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
458