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/window.hxx>
21 #include <vcl/seleng.hxx>
22 #include <tools/debug.hxx>
23 #include <comphelper/lok.hxx>
25 FunctionSet::~FunctionSet()
29 inline bool SelectionEngine::ShouldDeselect( bool bModifierKey1
) const
31 return eSelMode
!= SelectionMode::Multiple
|| !bModifierKey1
;
34 // TODO: throw out FunctionSet::SelectAtPoint
36 SelectionEngine::SelectionEngine( vcl::Window
* pWindow
, FunctionSet
* pFuncSet
) :
38 nUpdateInterval( SELENG_AUTOREPEAT_INTERVAL
)
40 eSelMode
= SelectionMode::Single
;
41 pFunctionSet
= pFuncSet
;
42 nFlags
= SelectionEngineFlags::EXPANDONMOVE
;
45 aWTimer
.SetTimeoutHdl( LINK( this, SelectionEngine
, ImpWatchDog
) );
46 aWTimer
.SetTimeout( nUpdateInterval
);
47 aWTimer
.SetDebugName( "vcl::SelectionEngine aWTimer" );
50 SelectionEngine::~SelectionEngine()
55 IMPL_LINK_NOARG(SelectionEngine
, ImpWatchDog
, Timer
*, void)
57 if ( !aArea
.IsInside( aLastMove
.GetPosPixel() ) )
58 SelMouseMove( aLastMove
);
61 void SelectionEngine::SetSelectionMode( SelectionMode eMode
)
66 void SelectionEngine::CursorPosChanging( bool bShift
, bool bMod1
)
71 if ( bShift
&& eSelMode
!= SelectionMode::Single
)
75 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
77 pFunctionSet
->CreateAnchor();
78 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
83 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
85 if( ShouldDeselect( bMod1
) )
86 pFunctionSet
->DeselectAll();
87 pFunctionSet
->CreateAnchor();
88 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
96 if ( nFlags
& SelectionEngineFlags::HAS_ANCH
)
98 // pFunctionSet->CreateCursor();
99 pFunctionSet
->DestroyAnchor();
100 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
);
105 if( ShouldDeselect( bMod1
) )
106 pFunctionSet
->DeselectAll();
108 pFunctionSet
->DestroyAnchor();
109 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
);
114 bool SelectionEngine::SelMouseButtonDown( const MouseEvent
& rMEvt
)
116 nFlags
&= (~SelectionEngineFlags::CMDEVT
);
117 if ( !pFunctionSet
|| !pWin
|| rMEvt
.GetClicks() > 1 || rMEvt
.IsRight() )
120 sal_uInt16 nModifier
= rMEvt
.GetModifier() | nLockedMods
;
121 if ( nModifier
& KEY_MOD2
)
123 // in SingleSelection: filter Control-Key,
124 // so that a D&D can be also started with a Ctrl-Click
125 if ( nModifier
== KEY_MOD1
&& eSelMode
== SelectionMode::Single
)
128 Point aPos
= rMEvt
.GetPosPixel();
131 if( !rMEvt
.IsRight() )
133 pWin
->CaptureMouse();
134 nFlags
|= SelectionEngineFlags::IN_SEL
;
143 case 0: // KEY_NO_KEY
145 bool bSelAtPoint
= pFunctionSet
->IsSelectionAtPoint( aPos
);
146 nFlags
&= (~SelectionEngineFlags::IN_ADD
);
147 if ( (nFlags
& SelectionEngineFlags::DRG_ENAB
) && bSelAtPoint
)
149 nFlags
|= SelectionEngineFlags::WAIT_UPEVT
;
150 nFlags
&= ~(SelectionEngineFlags::IN_SEL
);
151 pWin
->ReleaseMouse();
152 return true; // wait for STARTDRAG-Command-Event
154 if ( eSelMode
!= SelectionMode::Single
)
157 pFunctionSet
->DeselectAll();
159 pFunctionSet
->DestroyAnchor();
160 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
); // bHasAnchor = false;
162 pFunctionSet
->SetCursorAtPoint( aPos
);
163 // special case Single-Selection, to enable simple Select+Drag
164 if (eSelMode
== SelectionMode::Single
&& (nFlags
& SelectionEngineFlags::DRG_ENAB
))
165 nFlags
|= SelectionEngineFlags::WAIT_UPEVT
;
170 if ( eSelMode
== SelectionMode::Single
)
172 pWin
->ReleaseMouse();
173 nFlags
&= (~SelectionEngineFlags::IN_SEL
);
176 if ( nFlags
& SelectionEngineFlags::ADD_ALW
)
177 nFlags
|= SelectionEngineFlags::IN_ADD
;
179 nFlags
&= (~SelectionEngineFlags::IN_ADD
);
181 if( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
183 if ( !(nFlags
& SelectionEngineFlags::IN_ADD
) )
184 pFunctionSet
->DeselectAll();
185 pFunctionSet
->CreateAnchor();
186 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
188 pFunctionSet
->SetCursorAtPoint( aPos
);
192 // allow Control only for Multi-Select
193 if ( eSelMode
!= SelectionMode::Multiple
)
195 nFlags
&= (~SelectionEngineFlags::IN_SEL
);
196 pWin
->ReleaseMouse();
197 return true; // skip Mouse-Click
199 if ( nFlags
& SelectionEngineFlags::HAS_ANCH
)
201 // pFunctionSet->CreateCursor();
202 pFunctionSet
->DestroyAnchor();
203 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
);
205 if ( pFunctionSet
->IsSelectionAtPoint( aPos
) )
207 pFunctionSet
->DeselectAtPoint( aPos
);
208 pFunctionSet
->SetCursorAtPoint( aPos
, true );
212 pFunctionSet
->SetCursorAtPoint( aPos
);
216 case KEY_SHIFT
+ KEY_MOD1
:
217 if ( eSelMode
!= SelectionMode::Multiple
)
219 pWin
->ReleaseMouse();
220 nFlags
&= (~SelectionEngineFlags::IN_SEL
);
223 nFlags
|= SelectionEngineFlags::IN_ADD
; //bIsInAddMode = true;
224 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
226 pFunctionSet
->CreateAnchor();
227 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
229 pFunctionSet
->SetCursorAtPoint( aPos
);
236 bool SelectionEngine::SelMouseButtonUp( const MouseEvent
& rMEvt
)
239 if( !pFunctionSet
|| !pWin
)
241 const SelectionEngineFlags nMask
= (SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
);
246 if( !rMEvt
.IsRight() )
251 if( (nFlags
& SelectionEngineFlags::WAIT_UPEVT
) && !(nFlags
& SelectionEngineFlags::CMDEVT
) &&
252 eSelMode
!= SelectionMode::Single
)
254 // MouseButtonDown in Sel but no CommandEvent yet
256 sal_uInt16 nModifier
= aLastMove
.GetModifier() | nLockedMods
;
257 if( nModifier
== KEY_MOD1
|| IsAlwaysAdding() )
259 if( !(nModifier
& KEY_SHIFT
) )
261 pFunctionSet
->DestroyAnchor();
262 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
); // uncheck anchor
264 pFunctionSet
->DeselectAtPoint( aLastMove
.GetPosPixel() );
265 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
); // uncheck anchor
266 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel(), true );
270 pFunctionSet
->DeselectAll();
271 nFlags
&= (~SelectionEngineFlags::HAS_ANCH
); // uncheck anchor
272 pFunctionSet
->SetCursorAtPoint( aLastMove
.GetPosPixel() );
276 const SelectionEngineFlags nMask
= (SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
| SelectionEngineFlags::IN_SEL
);
281 void SelectionEngine::ReleaseMouse()
283 if (!pWin
|| !pWin
->IsMouseCaptured())
285 pWin
->ReleaseMouse();
288 bool SelectionEngine::SelMouseMove( const MouseEvent
& rMEvt
)
291 if ( !pFunctionSet
|| !(nFlags
& SelectionEngineFlags::IN_SEL
) ||
292 (nFlags
& (SelectionEngineFlags::CMDEVT
| SelectionEngineFlags::WAIT_UPEVT
)) )
295 if( !(nFlags
& SelectionEngineFlags::EXPANDONMOVE
) )
296 return false; // wait for DragEvent!
299 // if the mouse is outside the area, the frequency of
300 // SetCursorAtPoint() is only set by the Timer
301 if( aWTimer
.IsActive() && !aArea
.IsInside( rMEvt
.GetPosPixel() ))
304 aWTimer
.SetTimeout( nUpdateInterval
);
305 if (!comphelper::LibreOfficeKit::isActive())
306 // Generating fake mouse moves does not work with LOK.
308 if ( eSelMode
!= SelectionMode::Single
)
310 if ( !(nFlags
& SelectionEngineFlags::HAS_ANCH
) )
312 pFunctionSet
->CreateAnchor();
313 nFlags
|= SelectionEngineFlags::HAS_ANCH
;
317 pFunctionSet
->SetCursorAtPoint( rMEvt
.GetPosPixel() );
322 void SelectionEngine::SetWindow( vcl::Window
* pNewWin
)
324 if( pNewWin
!= pWin
)
326 if ( pWin
&& (nFlags
& SelectionEngineFlags::IN_SEL
) )
327 pWin
->ReleaseMouse();
329 if ( pWin
&& ( nFlags
& SelectionEngineFlags::IN_SEL
) )
330 pWin
->CaptureMouse();
334 void SelectionEngine::Reset()
337 if ( nFlags
& SelectionEngineFlags::IN_SEL
)
338 pWin
->ReleaseMouse();
339 SelectionEngineFlags nMask
= (SelectionEngineFlags::HAS_ANCH
| SelectionEngineFlags::IN_SEL
);
344 void SelectionEngine::Command( const CommandEvent
& rCEvt
)
346 // Timer aWTimer is active during enlarging a selection
347 if ( !pFunctionSet
|| !pWin
|| aWTimer
.IsActive() )
350 nFlags
|= SelectionEngineFlags::CMDEVT
;
351 if ( rCEvt
.GetCommand() == CommandEventId::StartDrag
)
353 if ( nFlags
& SelectionEngineFlags::DRG_ENAB
)
355 SAL_WARN_IF( !rCEvt
.IsMouseEvent(), "vcl", "STARTDRAG: Not a MouseEvent" );
356 if ( pFunctionSet
->IsSelectionAtPoint( rCEvt
.GetMousePosPixel() ) )
358 aLastMove
= MouseEvent( rCEvt
.GetMousePosPixel(),
359 aLastMove
.GetClicks(), aLastMove
.GetMode(),
360 aLastMove
.GetButtons(), aLastMove
.GetModifier() );
361 pFunctionSet
->BeginDrag();
362 const SelectionEngineFlags nMask
= (SelectionEngineFlags::CMDEVT
|SelectionEngineFlags::WAIT_UPEVT
|SelectionEngineFlags::IN_SEL
);
366 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
369 nFlags
&= ~SelectionEngineFlags::CMDEVT
;
373 void SelectionEngine::SetUpdateInterval( sal_uLong nInterval
)
375 if (nInterval
< SELENG_AUTOREPEAT_INTERVAL_MIN
)
376 // Set a lower threshold. On Windows, setting this value too low
377 // would cause selection to get updated indefinitely.
378 nInterval
= SELENG_AUTOREPEAT_INTERVAL_MIN
;
380 if (nUpdateInterval
== nInterval
)
384 if (aWTimer
.IsActive())
386 // reset the timer right away on interval change.
388 aWTimer
.SetTimeout(nInterval
);
392 aWTimer
.SetTimeout(nInterval
);
394 nUpdateInterval
= nInterval
;
397 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */