Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / window / seleng.cxx
blobcb3b19056bcde08e580e6de0b8dc2a7cf963a096
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 ) :
38 pWin( pWindow ),
39 nUpdateInterval( SELENG_AUTOREPEAT_INTERVAL )
41 eSelMode = SelectionMode::Single;
42 pFunctionSet = pFuncSet;
43 nFlags = SelectionEngineFlags::EXPANDONMOVE;
44 nLockedMods = 0;
46 aWTimer.SetInvokeHandler( LINK( this, SelectionEngine, ImpWatchDog ) );
47 aWTimer.SetTimeout( nUpdateInterval );
48 aWTimer.SetDebugName( "vcl::SelectionEngine aWTimer" );
51 SelectionEngine::~SelectionEngine()
53 aWTimer.Stop();
56 IMPL_LINK_NOARG(SelectionEngine, ImpWatchDog, Timer *, void)
58 if ( !aArea.IsInside( aLastMove.GetPosPixel() ) )
59 SelMouseMove( aLastMove );
62 void SelectionEngine::SetSelectionMode( SelectionMode eMode )
64 eSelMode = eMode;
67 void SelectionEngine::CursorPosChanging( bool bShift, bool bMod1 )
69 if ( !pFunctionSet )
70 return;
72 if ( bShift && eSelMode != SelectionMode::Single )
74 if ( IsAddMode() )
76 if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
78 pFunctionSet->CreateAnchor();
79 nFlags |= SelectionEngineFlags::HAS_ANCH;
82 else
84 if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
86 if( ShouldDeselect( bMod1 ) )
87 pFunctionSet->DeselectAll();
88 pFunctionSet->CreateAnchor();
89 nFlags |= SelectionEngineFlags::HAS_ANCH;
93 else
95 if ( IsAddMode() )
97 if ( nFlags & SelectionEngineFlags::HAS_ANCH )
99 // pFunctionSet->CreateCursor();
100 pFunctionSet->DestroyAnchor();
101 nFlags &= ~SelectionEngineFlags::HAS_ANCH;
104 else
106 if( ShouldDeselect( bMod1 ) )
107 pFunctionSet->DeselectAll();
108 else
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 )
119 return false;
121 sal_uInt16 nModifier = rMEvt.GetModifier() | nLockedMods;
122 if ( nModifier & KEY_MOD2 )
123 return false;
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 )
127 nModifier = 0;
129 Point aPos = rMEvt.GetPosPixel();
130 aLastMove = rMEvt;
132 if( !rMEvt.IsRight() )
134 CaptureMouse();
135 nFlags |= SelectionEngineFlags::IN_SEL;
137 else
139 nModifier = 0;
142 switch ( nModifier )
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;
152 ReleaseMouse();
153 return true; // wait for STARTDRAG-Command-Event
155 if ( eSelMode != SelectionMode::Single )
157 if( !IsAddMode() )
158 pFunctionSet->DeselectAll();
159 else
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;
167 return true;
170 case KEY_SHIFT:
171 if ( eSelMode == SelectionMode::Single )
173 ReleaseMouse();
174 nFlags &= ~SelectionEngineFlags::IN_SEL;
175 return false;
177 if ( nFlags & SelectionEngineFlags::ADD_ALW )
178 nFlags |= SelectionEngineFlags::IN_ADD;
179 else
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 );
190 return true;
192 case KEY_MOD1:
193 // allow Control only for Multi-Select
194 if ( eSelMode != SelectionMode::Multiple )
196 nFlags &= ~SelectionEngineFlags::IN_SEL;
197 ReleaseMouse();
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 );
211 else
213 pFunctionSet->SetCursorAtPoint( aPos );
215 return true;
217 case KEY_SHIFT + KEY_MOD1:
218 if ( eSelMode != SelectionMode::Multiple )
220 ReleaseMouse();
221 nFlags &= ~SelectionEngineFlags::IN_SEL;
222 return false;
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 );
231 return true;
234 return false;
237 bool SelectionEngine::SelMouseButtonUp( const MouseEvent& rMEvt )
239 aWTimer.Stop();
240 if (!pFunctionSet)
242 const SelectionEngineFlags nMask = SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT | SelectionEngineFlags::IN_SEL;
243 nFlags &= ~nMask;
244 return false;
247 if (!rMEvt.IsRight())
248 ReleaseMouse();
250 if( (nFlags & SelectionEngineFlags::WAIT_UPEVT) && !(nFlags & SelectionEngineFlags::CMDEVT) &&
251 eSelMode != SelectionMode::Single)
253 // MouseButtonDown in Sel but no CommandEvent yet
254 // ==> deselect
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 );
267 else
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;
276 nFlags &= ~nMask;
277 return true;
280 void SelectionEngine::ReleaseMouse()
282 if (!pWin || !pWin->IsMouseCaptured())
283 return;
284 pWin->ReleaseMouse();
287 void SelectionEngine::CaptureMouse()
289 if (!pWin || pWin->IsMouseCaptured())
290 return;
291 pWin->CaptureMouse();
294 bool SelectionEngine::SelMouseMove( const MouseEvent& rMEvt )
297 if ( !pFunctionSet || !(nFlags & SelectionEngineFlags::IN_SEL) ||
298 (nFlags & (SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT)) )
299 return false;
301 if( !(nFlags & SelectionEngineFlags::EXPANDONMOVE) )
302 return false; // wait for DragEvent!
304 aLastMove = rMEvt;
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() ))
308 return true;
310 aWTimer.SetTimeout( nUpdateInterval );
311 if (!comphelper::LibreOfficeKit::isActive())
312 // Generating fake mouse moves does not work with LOK.
313 aWTimer.Start();
314 if ( eSelMode != SelectionMode::Single )
316 if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
318 pFunctionSet->CreateAnchor();
319 nFlags |= SelectionEngineFlags::HAS_ANCH;
323 pFunctionSet->SetCursorAtPoint( rMEvt.GetPosPixel() );
325 return true;
328 void SelectionEngine::SetWindow( vcl::Window* pNewWin )
330 if( pNewWin != pWin )
332 if (nFlags & SelectionEngineFlags::IN_SEL)
333 ReleaseMouse();
334 pWin = pNewWin;
335 if (nFlags & SelectionEngineFlags::IN_SEL)
336 CaptureMouse();
340 void SelectionEngine::Reset()
342 aWTimer.Stop();
343 if (nFlags & SelectionEngineFlags::IN_SEL)
344 ReleaseMouse();
345 nFlags &= ~SelectionEngineFlags(SelectionEngineFlags::HAS_ANCH | SelectionEngineFlags::IN_SEL);
346 nLockedMods = 0;
349 void SelectionEngine::Command( const CommandEvent& rCEvt )
351 // Timer aWTimer is active during enlarging a selection
352 if ( !pFunctionSet || aWTimer.IsActive() )
353 return;
354 aWTimer.Stop();
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;
368 nFlags &= ~nMask;
370 else
371 nFlags &= ~SelectionEngineFlags::CMDEVT;
373 else
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)
386 // no update needed.
387 return;
389 if (aWTimer.IsActive())
391 // reset the timer right away on interval change.
392 aWTimer.Stop();
393 aWTimer.SetTimeout(nInterval);
394 aWTimer.Start();
396 else
397 aWTimer.SetTimeout(nInterval);
399 nUpdateInterval = nInterval;
402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */