1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <vcl/commandevent.hxx>
21 #include <vcl/window.hxx>
22 #include <vcl/seleng.hxx>
23 #include <comphelper/lok.hxx>
24 #include <sal/log.hxx>
26 FunctionSet::~FunctionSet()
30 inline bool SelectionEngine::ShouldDeselect( bool bModifierKey1
) const
32 return eSelMode
!= SelectionMode::Multiple
|| !bModifierKey1
;
35 // TODO: throw out FunctionSet::SelectAtPoint
37 SelectionEngine::SelectionEngine( vcl::Window
* pWindow
, FunctionSet
* pFuncSet
) :
39 aWTimer( "vcl::SelectionEngine aWTimer" ),
40 nUpdateInterval( SELENG_AUTOREPEAT_INTERVAL
)
42 eSelMode
= SelectionMode::Single
;
43 pFunctionSet
= pFuncSet
;
44 nFlags
= SelectionEngineFlags::EXPANDONMOVE
;
47 aWTimer
.SetInvokeHandler( LINK( this, SelectionEngine
, ImpWatchDog
) );
48 aWTimer
.SetTimeout( nUpdateInterval
);
51 SelectionEngine::~SelectionEngine()
56 IMPL_LINK_NOARG(SelectionEngine
, ImpWatchDog
, Timer
*, void)
58 if ( !aArea
.Contains( aLastMove
.GetPosPixel() ) )
59 SelMouseMove( aLastMove
);
62 void SelectionEngine::SetSelectionMode( SelectionMode eMode
)
67 void SelectionEngine::CursorPosChanging( bool bShift
, bool bMod1
)
72 if ( bShift
&& eSelMode
!= SelectionMode::Single
)
76 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
78 pFunctionSet
->CreateAnchor();
79 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
84 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
86 if( ShouldDeselect( bMod1
) )
87 pFunctionSet
->DeselectAll();
88 pFunctionSet
->CreateAnchor();
89 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
97 if ( nFlags
& SelectionEngineFlags::HAS_ANCH
)
99 // pFunctionSet->CreateCursor();
100 pFunctionSet
->DestroyAnchor();
101 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
;
106 if( ShouldDeselect( bMod1
) )
107 pFunctionSet
->DeselectAll();
109 pFunctionSet
->DestroyAnchor();
110 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
;
115 bool SelectionEngine::SelMouseButtonDown( const MouseEvent
& rMEvt
)
117 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
118 if ( !pFunctionSet
|| rMEvt
.GetClicks() > 1 )
121 sal_uInt16 nModifier
= rMEvt
.GetModifier() | nLockedMods
;
122 bool nSwap
= comphelper::LibreOfficeKit::isActive() && (nModifier
& KEY_MOD1
) && (nModifier
& KEY_MOD2
);
124 if ( !nSwap
&& (nModifier
& KEY_MOD2
) )
126 // in SingleSelection: filter Control-Key,
127 // so that a D&D can be also started with a Ctrl-Click
128 if ( nModifier
== KEY_MOD1
&& eSelMode
== SelectionMode::Single
)
131 Point aPos
= rMEvt
.GetPosPixel();
134 if( !rMEvt
.IsRight() )
137 nFlags
|= SelectionEngineFlags::IN_SEL
;
146 pFunctionSet
->CreateAnchor();
147 pFunctionSet
->SetCursorAtPoint( aPos
);
153 case 0: // KEY_NO_KEY
155 bool bSelAtPoint
= pFunctionSet
->IsSelectionAtPoint( aPos
);
156 nFlags
&= ~SelectionEngineFlags::IN_ADD
;
157 if ( (nFlags
& SelectionEngineFlags::DRG_ENAB
) && bSelAtPoint
)
159 nFlags
|= SelectionEngineFlags::WAIT_UPEVT
;
160 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
162 return true; // wait for STARTDRAG-Command-Event
164 if ( eSelMode
!= SelectionMode::Single
)
167 pFunctionSet
->DeselectAll();
169 pFunctionSet
->DestroyAnchor();
170 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // bHasAnchor = false;
172 pFunctionSet
->SetCursorAtPoint( aPos
);
173 // special case Single-Selection, to enable simple Select+Drag
174 if (eSelMode
== SelectionMode::Single
&& (nFlags
& SelectionEngineFlags::DRG_ENAB
))
175 nFlags
|= SelectionEngineFlags::WAIT_UPEVT
;
180 if ( eSelMode
== SelectionMode::Single
)
183 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
184 pFunctionSet
->SetCursorAtPoint(aPos
);
187 if ( nFlags
& SelectionEngineFlags::ADD_ALW
)
188 nFlags
|= SelectionEngineFlags::IN_ADD
;
190 nFlags
&= ~SelectionEngineFlags::IN_ADD
;
192 if( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
194 if ( !(nFlags
& SelectionEngineFlags::IN_ADD
) )
195 pFunctionSet
->DeselectAll();
196 pFunctionSet
->CreateAnchor();
197 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
199 pFunctionSet
->SetCursorAtPoint( aPos
);
203 // allow Control only for Multi-Select
204 if ( eSelMode
!= SelectionMode::Multiple
)
206 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
208 return true; // skip Mouse-Click
210 if ( nFlags
& SelectionEngineFlags::HAS_ANCH
)
212 // pFunctionSet->CreateCursor();
213 pFunctionSet
->DestroyAnchor();
214 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
;
216 if ( pFunctionSet
->IsSelectionAtPoint( aPos
) )
218 pFunctionSet
->DeselectAtPoint( aPos
);
219 pFunctionSet
->SetCursorAtPoint( aPos
, true );
223 pFunctionSet
->SetCursorAtPoint( aPos
);
227 case KEY_SHIFT
+ KEY_MOD1
:
228 if ( eSelMode
!= SelectionMode::Multiple
)
231 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
234 nFlags
|= SelectionEngineFlags::IN_ADD
; //bIsInAddMode = true;
235 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
237 pFunctionSet
->CreateAnchor();
238 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
240 pFunctionSet
->SetCursorAtPoint( aPos
);
247 bool SelectionEngine::SelMouseButtonUp( const MouseEvent
& rMEvt
)
252 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
;
257 if (!rMEvt
.IsRight())
260 #if defined IOS || defined ANDROID
261 const bool bDoMessWithSelection
= !rMEvt
.IsRight();
263 constexpr bool bDoMessWithSelection
= true;
266 if( (nFlags
& SelectionEngineFlags::WAIT_UPEVT
) && !(nFlags
& SelectionEngineFlags::CMDEVT
) &&
267 eSelMode
!= SelectionMode::Single
)
269 // MouseButtonDown in Sel but no CommandEvent yet
271 sal_uInt16 nModifier
= aLastMove
.GetModifier() | nLockedMods
;
272 if( nModifier
== KEY_MOD1
|| IsAlwaysAdding() )
274 if( !(nModifier
& KEY_SHIFT
) )
276 pFunctionSet
->DestroyAnchor();
277 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
279 pFunctionSet
->DeselectAtPoint( aLastMove
.GetPosPixel() );
280 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
281 if (bDoMessWithSelection
)
282 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel(), true );
286 if (bDoMessWithSelection
)
287 pFunctionSet
->DeselectAll();
288 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
289 if (bDoMessWithSelection
)
290 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel() );
294 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
;
299 void SelectionEngine::ReleaseMouse()
301 if (!pWin
|| !pWin
->IsMouseCaptured())
303 pWin
->ReleaseMouse();
306 void SelectionEngine::CaptureMouse()
308 if (!pWin
|| pWin
->IsMouseCaptured())
310 pWin
->CaptureMouse();
313 bool SelectionEngine::SelMouseMove( const MouseEvent
& rMEvt
)
316 if ( !pFunctionSet
|| !(nFlags
& SelectionEngineFlags::IN_SEL
) ||
317 (nFlags
& (SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
)) )
320 if( !(nFlags
& SelectionEngineFlags::EXPANDONMOVE
) )
321 return false; // wait for DragEvent!
324 // if the mouse is outside the area, the frequency of
325 // SetCursorAtPoint() is only set by the Timer
326 if( aWTimer
.IsActive() && !aArea
.Contains( rMEvt
.GetPosPixel() ))
329 aWTimer
.SetTimeout( nUpdateInterval
);
330 if (!comphelper::LibreOfficeKit::isActive())
331 // Generating fake mouse moves does not work with LOK.
333 if ( eSelMode
!= SelectionMode::Single
)
335 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
337 pFunctionSet
->CreateAnchor();
338 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
342 pFunctionSet
->SetCursorAtPoint( rMEvt
.GetPosPixel() );
347 void SelectionEngine::SetWindow( vcl::Window
* pNewWin
)
349 if( pNewWin
!= pWin
)
351 if (nFlags
& SelectionEngineFlags::IN_SEL
)
354 if (nFlags
& SelectionEngineFlags::IN_SEL
)
359 void SelectionEngine::Reset()
362 if (nFlags
& SelectionEngineFlags::IN_SEL
)
364 nFlags
&= ~SelectionEngineFlags(SelectionEngineFlags::HAS_ANCH
| SelectionEngineFlags::IN_SEL
);
368 bool SelectionEngine::Command( const CommandEvent
& rCEvt
)
370 // Timer aWTimer is active during enlarging a selection
371 if ( !pFunctionSet
|| aWTimer
.IsActive() )
374 if ( rCEvt
.GetCommand() != CommandEventId::StartDrag
)
377 nFlags
|= SelectionEngineFlags::CMDEVT
;
378 if ( nFlags
& SelectionEngineFlags::DRG_ENAB
)
380 SAL_WARN_IF( !rCEvt
.IsMouseEvent(), "vcl", "STARTDRAG: Not a MouseEvent" );
381 if ( pFunctionSet
->IsSelectionAtPoint( rCEvt
.GetMousePosPixel() ) )
383 aLastMove
= MouseEvent( rCEvt
.GetMousePosPixel(),
384 aLastMove
.GetClicks(), aLastMove
.GetMode(),
385 aLastMove
.GetButtons(), aLastMove
.GetModifier() );
386 pFunctionSet
->BeginDrag();
387 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
|SelectionEngineFlags::WAIT_UPEVT
|SelectionEngineFlags::IN_SEL
;
391 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
394 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
398 void SelectionEngine::SetUpdateInterval( sal_uLong nInterval
)
400 if (nInterval
< SELENG_AUTOREPEAT_INTERVAL_MIN
)
401 // Set a lower threshold. On Windows, setting this value too low
402 // would cause selection to get updated indefinitely.
403 nInterval
= SELENG_AUTOREPEAT_INTERVAL_MIN
;
405 if (nUpdateInterval
== nInterval
)
409 if (aWTimer
.IsActive())
411 // reset the timer right away on interval change.
413 aWTimer
.SetTimeout(nInterval
);
417 aWTimer
.SetTimeout(nInterval
);
419 nUpdateInterval
= nInterval
;
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */