Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/window/scrwnd.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <limits.h>
21
22
#include <o3tl/float_int_conversion.hxx>
23
#include <tools/time.hxx>
24
25
#include <bitmaps.hlst>
26
#include <svdata.hxx>
27
#include <scrwnd.hxx>
28
29
#include <vcl/timer.hxx>
30
#include <vcl/commandevent.hxx>
31
#include <vcl/event.hxx>
32
#include <vcl/ptrstyle.hxx>
33
#include <sal/log.hxx>
34
35
#include <math.h>
36
37
0
#define WHEEL_WIDTH     25
38
0
#define WHEEL_RADIUS    ((WHEEL_WIDTH) >> 1 )
39
0
#define MAX_TIME        300
40
0
#define MIN_TIME        20
41
0
#define DEF_TIMEOUT     50
42
43
ImplWheelWindow::ImplWheelWindow( vcl::Window* pParent ) :
44
0
            FloatingWindow  ( pParent, 0 ),
45
0
            mnRepaintTime   ( 1 ),
46
0
            mnTimeout       ( DEF_TIMEOUT ),
47
0
            mnWheelMode     ( WheelMode::NONE ),
48
0
            mnActDist       ( 0 ),
49
0
            mnStepDeltaX    ( 0 ),
50
0
            mnStepDeltaY    ( 0 ),
51
0
            mnActDeltaX     ( 0 ),
52
0
            mnActDeltaY     ( 0 )
53
0
{
54
    // we need a parent
55
0
    assert(pParent && "ImplWheelWindow::ImplWheelWindow(): Parent not set!");
56
57
0
    const Size      aSize( pParent->GetOutputSizePixel() );
58
0
    const StartAutoScrollFlags nFlags = ImplGetSVData()->mpWinData->mnAutoScrollFlags;
59
0
    const bool      bHorz( nFlags & StartAutoScrollFlags::Horz );
60
0
    const bool      bVert( nFlags & StartAutoScrollFlags::Vert );
61
62
    // calculate maximum speed distance
63
0
    mnMaxWidth = static_cast<sal_uLong>( 0.4 * hypot( static_cast<double>(aSize.Width()), aSize.Height() ) );
64
65
    // create wheel window
66
0
    SetTitleType( FloatWinTitleType::NONE );
67
0
    ImplCreateImageList();
68
0
    Bitmap aBmp(SV_RESID_BITMAP_SCROLLMSK);
69
0
    ImplSetRegion(aBmp);
70
71
    // set wheel mode
72
0
    if( bHorz && bVert )
73
0
        ImplSetWheelMode( WheelMode::VH );
74
0
    else if( bHorz )
75
0
        ImplSetWheelMode( WheelMode::H );
76
0
    else
77
0
        ImplSetWheelMode( WheelMode::V );
78
79
    // init timer
80
0
    mpTimer.reset(new Timer("WheelWindowTimer"));
81
0
    mpTimer->SetInvokeHandler( LINK( this, ImplWheelWindow, ImplScrollHdl ) );
82
0
    mpTimer->SetTimeout( mnTimeout );
83
0
    mpTimer->Start();
84
85
0
    CaptureMouse();
86
0
}
Unexecuted instantiation: ImplWheelWindow::ImplWheelWindow(vcl::Window*)
Unexecuted instantiation: ImplWheelWindow::ImplWheelWindow(vcl::Window*)
87
88
ImplWheelWindow::~ImplWheelWindow()
89
0
{
90
0
    disposeOnce();
91
0
}
92
93
void ImplWheelWindow::dispose()
94
0
{
95
0
    ImplStop();
96
0
    mpTimer.reset();
97
0
    FloatingWindow::dispose();
98
0
}
99
100
void ImplWheelWindow::ImplStop()
101
0
{
102
0
    ReleaseMouse();
103
0
    mpTimer->Stop();
104
0
    Show(false);
105
0
}
106
107
void ImplWheelWindow::ImplSetRegion( const Bitmap& rRegionBmp )
108
0
{
109
0
    Point           aPos( GetPointerPosPixel() );
110
0
    const Size      aSize( rRegionBmp.GetSizePixel() );
111
0
    const tools::Rectangle aRect( Point(), aSize );
112
113
0
    maCenter = maLastMousePos = aPos;
114
0
    aPos.AdjustX( -(aSize.Width() >> 1) );
115
0
    aPos.AdjustY( -(aSize.Height() >> 1) );
116
117
0
    SetPosSizePixel( aPos, aSize );
118
0
    SetWindowRegionPixel( rRegionBmp.CreateRegion( COL_BLACK, aRect ) );
119
0
}
120
121
void ImplWheelWindow::ImplCreateImageList()
122
0
{
123
0
    maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_SCROLLVH);
124
0
    maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_SCROLLV);
125
0
    maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_SCROLLH);
126
0
    maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_WHEELVH);
127
0
    maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_WHEELV);
128
0
    maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_WHEELH);
129
0
}
130
131
void ImplWheelWindow::ImplSetWheelMode( WheelMode nWheelMode )
132
0
{
133
0
    if( nWheelMode == mnWheelMode )
134
0
        return;
135
136
0
    mnWheelMode = nWheelMode;
137
138
0
    if( WheelMode::NONE == mnWheelMode )
139
0
    {
140
0
        if( IsVisible() )
141
0
            Hide();
142
0
    }
143
0
    else
144
0
    {
145
0
        if( !IsVisible() )
146
0
            Show();
147
148
0
        Invalidate();
149
0
    }
150
0
}
151
152
void ImplWheelWindow::ImplDrawWheel(vcl::RenderContext& rRenderContext)
153
0
{
154
0
    int nIndex;
155
156
0
    switch (mnWheelMode)
157
0
    {
158
0
        case WheelMode::VH:
159
0
            nIndex = 0;
160
0
        break;
161
0
        case WheelMode::V:
162
0
            nIndex = 1;
163
0
        break;
164
0
        case WheelMode::H:
165
0
            nIndex = 2;
166
0
        break;
167
0
        case WheelMode::ScrollVH:
168
0
            nIndex = 3;
169
0
        break;
170
0
        case WheelMode::ScrollV:
171
0
            nIndex = 4;
172
0
        break;
173
0
        case WheelMode::ScrollH:
174
0
            nIndex = 5;
175
0
        break;
176
0
        default:
177
0
            nIndex = -1;
178
0
        break;
179
0
    }
180
181
0
    if (nIndex >= 0)
182
0
        rRenderContext.DrawImage(Point(), maImgList[nIndex]);
183
0
}
184
185
void ImplWheelWindow::ImplRecalcScrollValues()
186
0
{
187
0
    if( mnActDist < WHEEL_RADIUS )
188
0
    {
189
0
        mnActDeltaX = mnActDeltaY = 0;
190
0
        mnTimeout = DEF_TIMEOUT;
191
0
    }
192
0
    else
193
0
    {
194
0
        sal_uInt64 nCurTime;        // Scrolling time interval
195
196
        // calc current time
197
0
        if( mnMaxWidth )
198
0
        {
199
            // Time interval for each unit of scrolling. Mouse further from start point -> shorter time interval -> faster scrolling.
200
0
            const double fExp = ( static_cast<double>(mnActDist) / mnMaxWidth ) * log10( double(MAX_TIME) / MIN_TIME );
201
0
            nCurTime = static_cast<sal_uInt64>( MAX_TIME / pow( 10., fExp ) );
202
0
        }
203
0
        else
204
0
            nCurTime = MAX_TIME;
205
206
0
        if( !nCurTime )
207
0
            nCurTime = 1;
208
209
0
        if( mnRepaintTime <= nCurTime )     // Draw time less than scroll time interval
210
0
        {
211
0
            mnActDeltaX = mnStepDeltaX;     // Scroll 1 unit
212
0
            mnActDeltaY = mnStepDeltaY;
213
0
            mnTimeout = nCurTime - mnRepaintTime;       // Call handler again after remaining interval elapsed
214
0
        }
215
0
        else    // Draw time greater than scroll time
216
0
        {
217
0
            sal_uInt64 nMult = mnRepaintTime / nCurTime;        // Scroll # units based on how many scroll intervals elapsed
218
219
0
            if( !( mnRepaintTime % nCurTime ) )
220
0
                mnTimeout = 0;
221
0
            else
222
0
                mnTimeout = ++nMult * nCurTime - mnRepaintTime;
223
224
0
            double fValX = static_cast<double>(mnStepDeltaX) * nMult;       // Get scroll distance from # units * step
225
0
            double fValY = static_cast<double>(mnStepDeltaY) * nMult;
226
227
0
            mnActDeltaX = o3tl::saturating_cast<tools::Long>(fValX);
228
0
            mnActDeltaY = o3tl::saturating_cast<tools::Long>(fValY);
229
0
        }
230
0
    }
231
0
}
232
233
PointerStyle ImplWheelWindow::ImplGetMousePointer( tools::Long nDistX, tools::Long nDistY ) const
234
0
{
235
0
    PointerStyle    eStyle;
236
0
    const StartAutoScrollFlags nFlags = ImplGetSVData()->mpWinData->mnAutoScrollFlags;
237
0
    const bool      bHorz( nFlags & StartAutoScrollFlags::Horz );
238
0
    const bool      bVert( nFlags & StartAutoScrollFlags::Vert );
239
240
0
    if( bHorz || bVert )
241
0
    {
242
0
        if( mnActDist < WHEEL_RADIUS )
243
0
        {
244
0
            if( bHorz && bVert )
245
0
                eStyle = PointerStyle::AutoScrollNSWE;
246
0
            else if( bHorz )
247
0
                eStyle = PointerStyle::AutoScrollWE;
248
0
            else
249
0
                eStyle = PointerStyle::AutoScrollNS;
250
0
        }
251
0
        else
252
0
        {
253
0
            double fAngle = basegfx::rad2deg(atan2(static_cast<double>(-nDistY), nDistX));
254
255
0
            if( fAngle < 0.0 )
256
0
                fAngle += 360.;
257
258
0
            if( bHorz && bVert )
259
0
            {
260
0
                if( fAngle >= 22.5 && fAngle <= 67.5 )
261
0
                    eStyle = PointerStyle::AutoScrollNE;
262
0
                else if( fAngle >= 67.5 && fAngle <= 112.5 )
263
0
                    eStyle = PointerStyle::AutoScrollN;
264
0
                else if( fAngle >= 112.5 && fAngle <= 157.5 )
265
0
                    eStyle = PointerStyle::AutoScrollNW;
266
0
                else if( fAngle >= 157.5 && fAngle <= 202.5 )
267
0
                    eStyle = PointerStyle::AutoScrollW;
268
0
                else if( fAngle >= 202.5 && fAngle <= 247.5 )
269
0
                    eStyle = PointerStyle::AutoScrollSW;
270
0
                else if( fAngle >= 247.5 && fAngle <= 292.5 )
271
0
                    eStyle = PointerStyle::AutoScrollS;
272
0
                else if( fAngle >= 292.5 && fAngle <= 337.5 )
273
0
                    eStyle = PointerStyle::AutoScrollSE;
274
0
                else
275
0
                    eStyle = PointerStyle::AutoScrollE;
276
0
            }
277
0
            else if( bHorz )
278
0
            {
279
0
                if( fAngle >= 270. || fAngle <= 90. )
280
0
                    eStyle = PointerStyle::AutoScrollE;
281
0
                else
282
0
                    eStyle = PointerStyle::AutoScrollW;
283
0
            }
284
0
            else
285
0
            {
286
0
                if( fAngle >= 0. && fAngle <= 180. )
287
0
                    eStyle = PointerStyle::AutoScrollN;
288
0
                else
289
0
                    eStyle = PointerStyle::AutoScrollS;
290
0
            }
291
0
        }
292
0
    }
293
0
    else
294
0
        eStyle = PointerStyle::Arrow;
295
296
0
    return eStyle;
297
0
}
298
299
void ImplWheelWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
300
0
{
301
0
    ImplDrawWheel(rRenderContext);
302
0
}
303
304
void ImplWheelWindow::MouseMove( const MouseEvent& rMEvt )
305
0
{
306
0
    FloatingWindow::MouseMove( rMEvt );
307
308
0
    const Point aMousePos( OutputToScreenPixel( rMEvt.GetPosPixel() ) );
309
0
    const tools::Long  nDistX = aMousePos.X() - maCenter.X();
310
0
    const tools::Long  nDistY = aMousePos.Y() - maCenter.Y();
311
312
0
    mnActDist = static_cast<sal_uLong>(hypot( static_cast<double>(nDistX), nDistY ));
313
314
0
    const PointerStyle  eActStyle = ImplGetMousePointer( nDistX, nDistY );
315
0
    const StartAutoScrollFlags nFlags = ImplGetSVData()->mpWinData->mnAutoScrollFlags;
316
0
    const bool          bHorz( nFlags & StartAutoScrollFlags::Horz );
317
0
    const bool          bVert( nFlags & StartAutoScrollFlags::Vert );
318
0
    const bool          bOuter = mnActDist > WHEEL_RADIUS;      // More than minimum distance from start point?
319
320
0
    if( maLastMousePos != aMousePos )
321
0
    {
322
0
        if( bOuter )    // More than minimum distance
323
0
        {
324
0
            switch( eActStyle )
325
0
            {
326
0
                case PointerStyle::AutoScrollN:   mnStepDeltaX = +0; mnStepDeltaY = +1; break;
327
0
                case PointerStyle::AutoScrollS:   mnStepDeltaX = +0; mnStepDeltaY = -1; break;
328
0
                case PointerStyle::AutoScrollW:   mnStepDeltaX = +1; mnStepDeltaY = +0; break;
329
0
                case PointerStyle::AutoScrollE:   mnStepDeltaX = -1; mnStepDeltaY = +0; break;
330
0
                case PointerStyle::AutoScrollNW:  mnStepDeltaX = +1; mnStepDeltaY = +1; break;
331
0
                case PointerStyle::AutoScrollNE:  mnStepDeltaX = -1; mnStepDeltaY = +1; break;
332
0
                case PointerStyle::AutoScrollSW:  mnStepDeltaX = +1; mnStepDeltaY = -1; break;
333
0
                case PointerStyle::AutoScrollSE:  mnStepDeltaX = -1; mnStepDeltaY = -1; break;
334
335
0
                default:
336
0
                    mnStepDeltaX = 0; mnStepDeltaY = 0;
337
0
                break;
338
0
            }
339
0
        }
340
0
        else    // Less than minimum distance
341
0
        {
342
0
            mnStepDeltaX = 0;
343
0
            mnStepDeltaY = 0;
344
0
        }
345
0
    }
346
347
0
    ImplRecalcScrollValues();
348
0
    maLastMousePos = aMousePos;
349
0
    SetPointer( eActStyle );
350
351
0
    if( bHorz && bVert )
352
0
        ImplSetWheelMode( bOuter ? WheelMode::ScrollVH : WheelMode::VH );
353
0
    else if( bHorz )
354
0
        ImplSetWheelMode( bOuter ? WheelMode::ScrollH : WheelMode::H );
355
0
    else
356
0
        ImplSetWheelMode( bOuter ? WheelMode::ScrollV : WheelMode::V );
357
0
}
358
359
void ImplWheelWindow::MouseButtonUp( const MouseEvent& rMEvt )
360
0
{
361
0
    if( mnActDist > WHEEL_RADIUS )
362
0
        GetParent()->EndAutoScroll();
363
0
    else
364
0
        FloatingWindow::MouseButtonUp( rMEvt );
365
0
}
366
367
IMPL_LINK_NOARG(ImplWheelWindow, ImplScrollHdl, Timer *, void)
368
0
{
369
0
    if ( mnActDeltaX || mnActDeltaY )
370
0
    {
371
0
        vcl::Window*             pWindow = GetParent();
372
0
        const Point         aMousePos( pWindow->OutputToScreenPixel( pWindow->GetPointerPosPixel() ) );
373
0
        Point               aCmdMousePos( pWindow->ScreenToOutputPixel( aMousePos ) );
374
0
        CommandScrollData   aScrollData( mnActDeltaX, mnActDeltaY );
375
0
        CommandEvent        aCEvt( aCmdMousePos, CommandEventId::AutoScroll, true, &aScrollData );
376
0
        NotifyEvent         aNCmdEvt( NotifyEventType::COMMAND, pWindow, &aCEvt );
377
378
0
        if ( !ImplCallPreNotify( aNCmdEvt ) )
379
0
        {
380
0
            const sal_uInt64 nTime = tools::Time::GetSystemTicks();
381
0
            VclPtr<ImplWheelWindow> xWin(this);
382
0
            pWindow->Command( aCEvt );
383
0
            if( xWin->isDisposed() )
384
0
                return;
385
0
            mnRepaintTime = std::max( tools::Time::GetSystemTicks() - nTime, sal_uInt64(1) );
386
0
            ImplRecalcScrollValues();
387
0
        }
388
0
    }
389
390
    // Call this handler again based on scrolling time interval
391
0
    if ( mnTimeout != mpTimer->GetTimeout() )
392
0
        mpTimer->SetTimeout( mnTimeout );
393
0
    mpTimer->Start();
394
0
}
395
396
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */