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 if ( nModifier
& KEY_MOD2
)
124 // in SingleSelection: filter Control-Key,
125 // so that a D&D can be also started with a Ctrl-Click
126 if ( nModifier
== KEY_MOD1
&& eSelMode
== SelectionMode::Single
)
129 Point aPos
= rMEvt
.GetPosPixel();
132 if( !rMEvt
.IsRight() )
135 nFlags
|= SelectionEngineFlags::IN_SEL
;
144 case 0: // KEY_NO_KEY
146 bool bSelAtPoint
= pFunctionSet
->IsSelectionAtPoint( aPos
);
147 nFlags
&= ~SelectionEngineFlags::IN_ADD
;
148 if ( (nFlags
& SelectionEngineFlags::DRG_ENAB
) && bSelAtPoint
)
150 nFlags
|= SelectionEngineFlags::WAIT_UPEVT
;
151 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
153 return true; // wait for STARTDRAG-Command-Event
155 if ( eSelMode
!= SelectionMode::Single
)
158 pFunctionSet
->DeselectAll();
160 pFunctionSet
->DestroyAnchor();
161 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // bHasAnchor = false;
163 pFunctionSet
->SetCursorAtPoint( aPos
);
164 // special case Single-Selection, to enable simple Select+Drag
165 if (eSelMode
== SelectionMode::Single
&& (nFlags
& SelectionEngineFlags::DRG_ENAB
))
166 nFlags
|= SelectionEngineFlags::WAIT_UPEVT
;
171 if ( eSelMode
== SelectionMode::Single
)
174 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
177 if ( nFlags
& SelectionEngineFlags::ADD_ALW
)
178 nFlags
|= SelectionEngineFlags::IN_ADD
;
180 nFlags
&= ~SelectionEngineFlags::IN_ADD
;
182 if( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
184 if ( !(nFlags
& SelectionEngineFlags::IN_ADD
) )
185 pFunctionSet
->DeselectAll();
186 pFunctionSet
->CreateAnchor();
187 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
189 pFunctionSet
->SetCursorAtPoint( aPos
);
193 // allow Control only for Multi-Select
194 if ( eSelMode
!= SelectionMode::Multiple
)
196 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
198 return true; // skip Mouse-Click
200 if ( nFlags
& SelectionEngineFlags::HAS_ANCH
)
202 // pFunctionSet->CreateCursor();
203 pFunctionSet
->DestroyAnchor();
204 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
;
206 if ( pFunctionSet
->IsSelectionAtPoint( aPos
) )
208 pFunctionSet
->DeselectAtPoint( aPos
);
209 pFunctionSet
->SetCursorAtPoint( aPos
, true );
213 pFunctionSet
->SetCursorAtPoint( aPos
);
217 case KEY_SHIFT
+ KEY_MOD1
:
218 if ( eSelMode
!= SelectionMode::Multiple
)
221 nFlags
&= ~SelectionEngineFlags::IN_SEL
;
224 nFlags
|= SelectionEngineFlags::IN_ADD
; //bIsInAddMode = true;
225 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
227 pFunctionSet
->CreateAnchor();
228 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
230 pFunctionSet
->SetCursorAtPoint( aPos
);
237 bool SelectionEngine::SelMouseButtonUp( const MouseEvent
& rMEvt
)
242 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
;
247 if (!rMEvt
.IsRight())
250 if( (nFlags
& SelectionEngineFlags::WAIT_UPEVT
) && !(nFlags
& SelectionEngineFlags::CMDEVT
) &&
251 eSelMode
!= SelectionMode::Single
)
253 // MouseButtonDown in Sel but no CommandEvent yet
255 sal_uInt16 nModifier
= aLastMove
.GetModifier() | nLockedMods
;
256 if( nModifier
== KEY_MOD1
|| IsAlwaysAdding() )
258 if( !(nModifier
& KEY_SHIFT
) )
260 pFunctionSet
->DestroyAnchor();
261 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
263 pFunctionSet
->DeselectAtPoint( aLastMove
.GetPosPixel() );
264 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
265 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel(), true );
269 pFunctionSet
->DeselectAll();
270 nFlags
&= ~SelectionEngineFlags::HAS_ANCH
; // uncheck anchor
271 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel() );
275 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
;
280 void SelectionEngine::ReleaseMouse()
282 if (!pWin
|| !pWin
->IsMouseCaptured())
284 pWin
->ReleaseMouse();
287 void SelectionEngine::CaptureMouse()
289 if (!pWin
|| pWin
->IsMouseCaptured())
291 pWin
->CaptureMouse();
294 bool SelectionEngine::SelMouseMove( const MouseEvent
& rMEvt
)
297 if ( !pFunctionSet
|| !(nFlags
& SelectionEngineFlags::IN_SEL
) ||
298 (nFlags
& (SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
)) )
301 if( !(nFlags
& SelectionEngineFlags::EXPANDONMOVE
) )
302 return false; // wait for DragEvent!
305 // if the mouse is outside the area, the frequency of
306 // SetCursorAtPoint() is only set by the Timer
307 if( aWTimer
.IsActive() && !aArea
.IsInside( rMEvt
.GetPosPixel() ))
310 aWTimer
.SetTimeout( nUpdateInterval
);
311 if (!comphelper::LibreOfficeKit::isActive())
312 // Generating fake mouse moves does not work with LOK.
314 if ( eSelMode
!= SelectionMode::Single
)
316 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
318 pFunctionSet
->CreateAnchor();
319 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
323 pFunctionSet
->SetCursorAtPoint( rMEvt
.GetPosPixel() );
328 void SelectionEngine::SetWindow( vcl::Window
* pNewWin
)
330 if( pNewWin
!= pWin
)
332 if (nFlags
& SelectionEngineFlags::IN_SEL
)
335 if (nFlags
& SelectionEngineFlags::IN_SEL
)
340 void SelectionEngine::Reset()
343 if (nFlags
& SelectionEngineFlags::IN_SEL
)
345 nFlags
&= ~SelectionEngineFlags(SelectionEngineFlags::HAS_ANCH
| SelectionEngineFlags::IN_SEL
);
349 void SelectionEngine::Command( const CommandEvent
& rCEvt
)
351 // Timer aWTimer is active during enlarging a selection
352 if ( !pFunctionSet
|| aWTimer
.IsActive() )
355 if ( rCEvt
.GetCommand() == CommandEventId::StartDrag
)
357 nFlags
|= SelectionEngineFlags::CMDEVT
;
358 if ( nFlags
& SelectionEngineFlags::DRG_ENAB
)
360 SAL_WARN_IF( !rCEvt
.IsMouseEvent(), "vcl", "STARTDRAG: Not a MouseEvent" );
361 if ( pFunctionSet
->IsSelectionAtPoint( rCEvt
.GetMousePosPixel() ) )
363 aLastMove
= MouseEvent( rCEvt
.GetMousePosPixel(),
364 aLastMove
.GetClicks(), aLastMove
.GetMode(),
365 aLastMove
.GetButtons(), aLastMove
.GetModifier() );
366 pFunctionSet
->BeginDrag();
367 const SelectionEngineFlags nMask
= SelectionEngineFlags::CMDEVT
|SelectionEngineFlags::WAIT_UPEVT
|SelectionEngineFlags::IN_SEL
;
371 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
374 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
378 void SelectionEngine::SetUpdateInterval( sal_uLong nInterval
)
380 if (nInterval
< SELENG_AUTOREPEAT_INTERVAL_MIN
)
381 // Set a lower threshold. On Windows, setting this value too low
382 // would cause selection to get updated indefinitely.
383 nInterval
= SELENG_AUTOREPEAT_INTERVAL_MIN
;
385 if (nUpdateInterval
== nInterval
)
389 if (aWTimer
.IsActive())
391 // reset the timer right away on interval change.
393 aWTimer
.SetTimeout(nInterval
);
397 aWTimer
.SetTimeout(nInterval
);
399 nUpdateInterval
= nInterval
;
402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */