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