build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / window / seleng.cxx
blobbe1c909b91a1f51ef8b2c78b66a26cd4e76570d3
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/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 ) :
37 pWin( pWindow ),
38 nUpdateInterval( SELENG_AUTOREPEAT_INTERVAL )
40 eSelMode = SelectionMode::Single;
41 pFunctionSet = pFuncSet;
42 nFlags = SelectionEngineFlags::EXPANDONMOVE;
43 nLockedMods = 0;
45 aWTimer.SetTimeoutHdl( LINK( this, SelectionEngine, ImpWatchDog ) );
46 aWTimer.SetTimeout( nUpdateInterval );
47 aWTimer.SetDebugName( "vcl::SelectionEngine aWTimer" );
50 SelectionEngine::~SelectionEngine()
52 aWTimer.Stop();
55 IMPL_LINK_NOARG(SelectionEngine, ImpWatchDog, Timer *, void)
57 if ( !aArea.IsInside( aLastMove.GetPosPixel() ) )
58 SelMouseMove( aLastMove );
61 void SelectionEngine::SetSelectionMode( SelectionMode eMode )
63 eSelMode = eMode;
66 void SelectionEngine::CursorPosChanging( bool bShift, bool bMod1 )
68 if ( !pFunctionSet )
69 return;
71 if ( bShift && eSelMode != SelectionMode::Single )
73 if ( IsAddMode() )
75 if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
77 pFunctionSet->CreateAnchor();
78 nFlags |= SelectionEngineFlags::HAS_ANCH;
81 else
83 if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
85 if( ShouldDeselect( bMod1 ) )
86 pFunctionSet->DeselectAll();
87 pFunctionSet->CreateAnchor();
88 nFlags |= SelectionEngineFlags::HAS_ANCH;
92 else
94 if ( IsAddMode() )
96 if ( nFlags & SelectionEngineFlags::HAS_ANCH )
98 // pFunctionSet->CreateCursor();
99 pFunctionSet->DestroyAnchor();
100 nFlags &= (~SelectionEngineFlags::HAS_ANCH);
103 else
105 if( ShouldDeselect( bMod1 ) )
106 pFunctionSet->DeselectAll();
107 else
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() )
118 return false;
120 sal_uInt16 nModifier = rMEvt.GetModifier() | nLockedMods;
121 if ( nModifier & KEY_MOD2 )
122 return false;
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 )
126 nModifier = 0;
128 Point aPos = rMEvt.GetPosPixel();
129 aLastMove = rMEvt;
131 if( !rMEvt.IsRight() )
133 pWin->CaptureMouse();
134 nFlags |= SelectionEngineFlags::IN_SEL;
136 else
138 nModifier = 0;
141 switch ( nModifier )
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 )
156 if( !IsAddMode() )
157 pFunctionSet->DeselectAll();
158 else
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;
166 return true;
169 case KEY_SHIFT:
170 if ( eSelMode == SelectionMode::Single )
172 pWin->ReleaseMouse();
173 nFlags &= (~SelectionEngineFlags::IN_SEL);
174 return false;
176 if ( nFlags & SelectionEngineFlags::ADD_ALW )
177 nFlags |= SelectionEngineFlags::IN_ADD;
178 else
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 );
189 return true;
191 case KEY_MOD1:
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 );
210 else
212 pFunctionSet->SetCursorAtPoint( aPos );
214 return true;
216 case KEY_SHIFT + KEY_MOD1:
217 if ( eSelMode != SelectionMode::Multiple )
219 pWin->ReleaseMouse();
220 nFlags &= (~SelectionEngineFlags::IN_SEL);
221 return false;
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 );
230 return true;
233 return false;
236 bool SelectionEngine::SelMouseButtonUp( const MouseEvent& rMEvt )
238 aWTimer.Stop();
239 if( !pFunctionSet || !pWin )
241 const SelectionEngineFlags nMask = (SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT | SelectionEngineFlags::IN_SEL);
242 nFlags &= ~nMask;
243 return false;
246 if( !rMEvt.IsRight() )
248 ReleaseMouse();
251 if( (nFlags & SelectionEngineFlags::WAIT_UPEVT) && !(nFlags & SelectionEngineFlags::CMDEVT) &&
252 eSelMode != SelectionMode::Single)
254 // MouseButtonDown in Sel but no CommandEvent yet
255 // ==> deselektieren
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 );
268 else
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);
277 nFlags &= ~nMask;
278 return true;
281 void SelectionEngine::ReleaseMouse()
283 if (!pWin || !pWin->IsMouseCaptured())
284 return;
285 pWin->ReleaseMouse();
288 bool SelectionEngine::SelMouseMove( const MouseEvent& rMEvt )
291 if ( !pFunctionSet || !(nFlags & SelectionEngineFlags::IN_SEL) ||
292 (nFlags & (SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT)) )
293 return false;
295 if( !(nFlags & SelectionEngineFlags::EXPANDONMOVE) )
296 return false; // wait for DragEvent!
298 aLastMove = rMEvt;
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() ))
302 return true;
304 aWTimer.SetTimeout( nUpdateInterval );
305 if (!comphelper::LibreOfficeKit::isActive())
306 // Generating fake mouse moves does not work with LOK.
307 aWTimer.Start();
308 if ( eSelMode != SelectionMode::Single )
310 if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
312 pFunctionSet->CreateAnchor();
313 nFlags |= SelectionEngineFlags::HAS_ANCH;
317 pFunctionSet->SetCursorAtPoint( rMEvt.GetPosPixel() );
319 return true;
322 void SelectionEngine::SetWindow( vcl::Window* pNewWin )
324 if( pNewWin != pWin )
326 if ( pWin && (nFlags & SelectionEngineFlags::IN_SEL) )
327 pWin->ReleaseMouse();
328 pWin = pNewWin;
329 if ( pWin && ( nFlags & SelectionEngineFlags::IN_SEL ) )
330 pWin->CaptureMouse();
334 void SelectionEngine::Reset()
336 aWTimer.Stop();
337 if ( nFlags & SelectionEngineFlags::IN_SEL )
338 pWin->ReleaseMouse();
339 SelectionEngineFlags nMask = (SelectionEngineFlags::HAS_ANCH | SelectionEngineFlags::IN_SEL);
340 nFlags &= ~nMask;
341 nLockedMods = 0;
344 void SelectionEngine::Command( const CommandEvent& rCEvt )
346 // Timer aWTimer is active during enlarging a selection
347 if ( !pFunctionSet || !pWin || aWTimer.IsActive() )
348 return;
349 aWTimer.Stop();
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);
363 nFlags &= ~nMask;
365 else
366 nFlags &= ~SelectionEngineFlags::CMDEVT;
368 else
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)
381 // no update needed.
382 return;
384 if (aWTimer.IsActive())
386 // reset the timer right away on interval change.
387 aWTimer.Stop();
388 aWTimer.SetTimeout(nInterval);
389 aWTimer.Start();
391 else
392 aWTimer.SetTimeout(nInterval);
394 nUpdateInterval = nInterval;
397 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */