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 nUpdateInterval( SELENG_AUTOREPEAT_INTERVAL
)
41 eSelMode
= SelectionMode::Single
;
42 pFunctionSet
= pFuncSet
;
43 nFlags
= SelectionEngineFlags::EXPANDONMOVE
;
46 aWTimer
.SetInvokeHandler( LINK( this, SelectionEngine
, ImpWatchDog
) );
47 aWTimer
.SetTimeout( nUpdateInterval
);
48 aWTimer
.SetDebugName( "vcl::SelectionEngine aWTimer" );
51 SelectionEngine::~SelectionEngine()
56 IMPL_LINK_NOARG(SelectionEngine
, ImpWatchDog
, Timer
*, void)
58 if ( !aArea
.IsInside( 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
;
186 if ( nFlags
& SelectionEngineFlags::ADD_ALW
)
187 nFlags
|= SelectionEngineFlags::IN_ADD
;
189 nFlags
&= ~SelectionEngineFlags::IN_ADD
;
191 if( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
193 if ( !(nFlags
& SelectionEngineFlags::IN_ADD
) )
194 pFunctionSet
->DeselectAll();
195 pFunctionSet
->CreateAnchor();
196 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
198 pFunctionSet
->SetCursorAtPoint( aPos
);
202 // allow Control only for Multi-Select
203 if ( eSelMode
!= SelectionMode::Multiple
)
205 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
207 return true; // skip Mouse-Click
209 if ( nFlags
& SelectionEngineFlags::HAS_ANCH
)
211 // pFunctionSet->CreateCursor();
212 pFunctionSet
->DestroyAnchor();
213 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
;
215 if ( pFunctionSet
->IsSelectionAtPoint( aPos
) )
217 pFunctionSet
->DeselectAtPoint( aPos
);
218 pFunctionSet
->SetCursorAtPoint( aPos
, true );
222 pFunctionSet
->SetCursorAtPoint( aPos
);
226 case KEY_SHIFT
+ KEY_MOD1
:
227 if ( eSelMode
!= SelectionMode::Multiple
)
230 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
233 nFlags
|= SelectionEngineFlags::IN_ADD
; //bIsInAddMode = true;
234 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
236 pFunctionSet
->CreateAnchor();
237 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
239 pFunctionSet
->SetCursorAtPoint( aPos
);
246 bool SelectionEngine::SelMouseButtonUp( const MouseEvent
& rMEvt
)
251 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
;
256 if (!rMEvt
.IsRight())
259 if( (nFlags
& SelectionEngineFlags::WAIT_UPEVT
) && !(nFlags
& SelectionEngineFlags::CMDEVT
) &&
260 eSelMode
!= SelectionMode::Single
)
262 // MouseButtonDown in Sel but no CommandEvent yet
264 sal_uInt16 nModifier
= aLastMove
.GetModifier() | nLockedMods
;
265 if( nModifier
== KEY_MOD1
|| IsAlwaysAdding() )
267 if( !(nModifier
& KEY_SHIFT
) )
269 pFunctionSet
->DestroyAnchor();
270 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
272 pFunctionSet
->DeselectAtPoint( aLastMove
.GetPosPixel() );
273 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
274 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel(), true );
278 pFunctionSet
->DeselectAll();
279 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
280 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel() );
284 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
;
289 void SelectionEngine::ReleaseMouse()
291 if (!pWin
|| !pWin
->IsMouseCaptured())
293 pWin
->ReleaseMouse();
296 void SelectionEngine::CaptureMouse()
298 if (!pWin
|| pWin
->IsMouseCaptured())
300 pWin
->CaptureMouse();
303 bool SelectionEngine::SelMouseMove( const MouseEvent
& rMEvt
)
306 if ( !pFunctionSet
|| !(nFlags
& SelectionEngineFlags::IN_SEL
) ||
307 (nFlags
& (SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
)) )
310 if( !(nFlags
& SelectionEngineFlags::EXPANDONMOVE
) )
311 return false; // wait for DragEvent!
314 // if the mouse is outside the area, the frequency of
315 // SetCursorAtPoint() is only set by the Timer
316 if( aWTimer
.IsActive() && !aArea
.IsInside( rMEvt
.GetPosPixel() ))
319 aWTimer
.SetTimeout( nUpdateInterval
);
320 if (!comphelper::LibreOfficeKit::isActive())
321 // Generating fake mouse moves does not work with LOK.
323 if ( eSelMode
!= SelectionMode::Single
)
325 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
327 pFunctionSet
->CreateAnchor();
328 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
332 pFunctionSet
->SetCursorAtPoint( rMEvt
.GetPosPixel() );
337 void SelectionEngine::SetWindow( vcl::Window
* pNewWin
)
339 if( pNewWin
!= pWin
)
341 if (nFlags
& SelectionEngineFlags::IN_SEL
)
344 if (nFlags
& SelectionEngineFlags::IN_SEL
)
349 void SelectionEngine::Reset()
352 if (nFlags
& SelectionEngineFlags::IN_SEL
)
354 nFlags
&= ~SelectionEngineFlags(SelectionEngineFlags::HAS_ANCH
| SelectionEngineFlags::IN_SEL
);
358 void SelectionEngine::Command( const CommandEvent
& rCEvt
)
360 // Timer aWTimer is active during enlarging a selection
361 if ( !pFunctionSet
|| aWTimer
.IsActive() )
364 if ( rCEvt
.GetCommand() != CommandEventId::StartDrag
)
367 nFlags
|= SelectionEngineFlags::CMDEVT
;
368 if ( nFlags
& SelectionEngineFlags::DRG_ENAB
)
370 SAL_WARN_IF( !rCEvt
.IsMouseEvent(), "vcl", "STARTDRAG: Not a MouseEvent" );
371 if ( pFunctionSet
->IsSelectionAtPoint( rCEvt
.GetMousePosPixel() ) )
373 aLastMove
= MouseEvent( rCEvt
.GetMousePosPixel(),
374 aLastMove
.GetClicks(), aLastMove
.GetMode(),
375 aLastMove
.GetButtons(), aLastMove
.GetModifier() );
376 pFunctionSet
->BeginDrag();
377 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
|SelectionEngineFlags::WAIT_UPEVT
|SelectionEngineFlags::IN_SEL
;
381 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
384 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
387 void SelectionEngine::SetUpdateInterval( sal_uLong nInterval
)
389 if (nInterval
< SELENG_AUTOREPEAT_INTERVAL_MIN
)
390 // Set a lower threshold. On Windows, setting this value too low
391 // would cause selection to get updated indefinitely.
392 nInterval
= SELENG_AUTOREPEAT_INTERVAL_MIN
;
394 if (nUpdateInterval
== nInterval
)
398 if (aWTimer
.IsActive())
400 // reset the timer right away on interval change.
402 aWTimer
.SetTimeout(nInterval
);
406 aWTimer
.SetTimeout(nInterval
);
408 nUpdateInterval
= nInterval
;
411 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */