Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/view/tabview.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 <scitems.hxx>
21
#include <sfx2/viewfrm.hxx>
22
#include <sfx2/bindings.hxx>
23
#include <vcl/commandevent.hxx>
24
#include <vcl/help.hxx>
25
#include <vcl/settings.hxx>
26
#include <vcl/cursor.hxx>
27
#include <sal/log.hxx>
28
#include <tools/svborder.hxx>
29
#include <tools/json_writer.hxx>
30
#include <o3tl/unit_conversion.hxx>
31
32
#include <pagedata.hxx>
33
#include <printfun.hxx>
34
#include <tabview.hxx>
35
#include <tabvwsh.hxx>
36
#include <document.hxx>
37
#include <gridwin.hxx>
38
#include <olinewin.hxx>
39
#include <olinetab.hxx>
40
#include <tabsplit.hxx>
41
#include <colrowba.hxx>
42
#include <tabcont.hxx>
43
#include <scmod.hxx>
44
#include <sc.hrc>
45
#include <strings.hrc>
46
#include <globstr.hrc>
47
#include <scresid.hxx>
48
#include <drawview.hxx>
49
#include <docsh.hxx>
50
#include <viewuno.hxx>
51
#include <appoptio.hxx>
52
#include <attrib.hxx>
53
#include <spellcheckcontext.hxx>
54
#include <comphelper/lok.hxx>
55
#include <sfx2/lokhelper.hxx>
56
#include <osl/diagnose.h>
57
58
#include <boost/property_tree/ptree.hpp>
59
#include <boost/property_tree/json_parser.hpp>
60
61
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
62
63
#include <algorithm>
64
65
#include <basegfx/utils/zoomtools.hxx>
66
67
0
#define SPLIT_MARGIN      30
68
0
#define SPLIT_HANDLE_SIZE 5
69
constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10;
70
71
0
#define SC_ICONSIZE     36
72
73
0
#define SC_SCROLLBAR_MIN 30
74
0
#define SC_TABBAR_MIN 6
75
76
using namespace ::com::sun::star;
77
78
//  Corner-Button
79
80
ScCornerButton::ScCornerButton( vcl::Window* pParent, ScViewData& rData ) :
81
0
    Window( pParent, WinBits( 0 ) ),
82
0
    rViewData( rData )
83
0
{
84
0
    ScCornerButton::EnableRTL( false );
85
0
    SetQuickHelpText(ScResId(SCSTR_QHELP_SELECT_ALL_CELLS));
86
0
}
Unexecuted instantiation: ScCornerButton::ScCornerButton(vcl::Window*, ScViewData&)
Unexecuted instantiation: ScCornerButton::ScCornerButton(vcl::Window*, ScViewData&)
87
88
ScCornerButton::~ScCornerButton()
89
0
{
90
0
}
91
92
void ScCornerButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
93
0
{
94
0
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
95
0
    SetBackground(rStyleSettings.GetFaceColor());
96
97
0
    Size aSize(GetOutputSizePixel());
98
0
    tools::Long nPosX = aSize.Width() - 1;
99
0
    tools::Long nPosY = aSize.Height() - 1;
100
101
0
    Window::Paint(rRenderContext, rRect);
102
103
0
    bool bLayoutRTL = rViewData.GetDocument().IsLayoutRTL( rViewData.CurrentTabForData() );
104
0
    tools::Long nDarkX = bLayoutRTL ? 0 : nPosX;
105
106
    //  both buttons have the same look now - only dark right/bottom lines
107
0
    rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
108
0
    rRenderContext.DrawLine(Point(0, nPosY), Point(nPosX, nPosY));
109
0
    rRenderContext.DrawLine(Point(nDarkX, 0), Point(nDarkX, nPosY));
110
0
}
111
112
void ScCornerButton::StateChanged( StateChangedType nType )
113
0
{
114
0
    Window::StateChanged( nType );
115
116
0
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
117
0
    SetBackground( rStyleSettings.GetFaceColor() );
118
0
    Invalidate();
119
0
}
120
121
void ScCornerButton::DataChanged( const DataChangedEvent& rDCEvt )
122
0
{
123
0
    Window::DataChanged( rDCEvt );
124
125
0
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
126
0
    SetBackground( rStyleSettings.GetFaceColor() );
127
0
    Invalidate();
128
0
}
129
130
void ScCornerButton::Resize()
131
0
{
132
0
    Invalidate();
133
0
}
134
135
void ScCornerButton::MouseButtonDown( const MouseEvent& rMEvt )
136
0
{
137
0
    ScModule* pScMod = ScModule::get();
138
0
    bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
139
0
    if (!bDisable)
140
0
    {
141
0
        ScTabViewShell* pViewSh = rViewData.GetViewShell();
142
0
        pViewSh->SetActive();                                   // Appear and SetViewFrame
143
        // tdf#84694 - deselect draw objects before selecting the entire sheet
144
0
        pViewSh->DrawDeselectAll();
145
0
        pViewSh->ActiveGrabFocus();
146
147
0
        bool bControl = rMEvt.IsMod1();
148
0
        pViewSh->SelectAll( bControl );
149
0
    }
150
0
}
151
namespace
152
{
153
154
bool lcl_HasColOutline( const ScViewData& rViewData )
155
0
{
156
0
    const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.CurrentTabForData());
157
0
    if (pTable)
158
0
    {
159
0
        const ScOutlineArray& rArray = pTable->GetColArray();
160
0
        if ( rArray.GetDepth() > 0 )
161
0
            return true;
162
0
    }
163
0
    return false;
164
0
}
165
166
bool lcl_HasRowOutline( const ScViewData& rViewData )
167
0
{
168
0
    const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.CurrentTabForData());
169
0
    if (pTable)
170
0
    {
171
0
        const ScOutlineArray& rArray = pTable->GetRowArray();
172
0
        if ( rArray.GetDepth() > 0 )
173
0
            return true;
174
0
    }
175
0
    return false;
176
0
}
177
178
} // anonymous namespace
179
180
ScTabView::ScTabView( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
181
0
    pFrameWin( pParent ),
182
0
    aViewData( rDocSh, pViewShell ),
183
0
    aFunctionSet( aViewData ),
184
0
    aHdrFunc( aViewData ),
185
0
    aVScrollTop( VclPtr<ScrollAdaptor>::Create( pFrameWin, false ) ),
186
0
    aVScrollBottom( VclPtr<ScrollAdaptor>::Create( pFrameWin, false ) ),
187
0
    aHScrollLeft( VclPtr<ScrollAdaptor>::Create( pFrameWin, true ) ),
188
0
    aHScrollRight( VclPtr<ScrollAdaptor>::Create( pFrameWin, true ) ),
189
0
    aCornerButton( VclPtr<ScCornerButton>::Create( pFrameWin, aViewData ) ),
190
0
    aTopButton( VclPtr<ScCornerButton>::Create( pFrameWin, aViewData ) ),
191
0
    aScrollTimer("ScTabView aScrollTimer"),
192
0
    pTimerWindow( nullptr ),
193
0
    aExtraEditViewManager( pViewShell, pGridWin ),
194
0
    nTipVisible( nullptr ),
195
0
    nTipAlign( QuickHelpFlags::NONE ),
196
0
    nPrevDragPos( 0 ),
197
0
    meBlockMode(None),
198
0
    meHighlightBlockMode(None),
199
0
    nBlockStartX( 0 ),
200
0
    nBlockStartXOrig( 0 ),
201
0
    nBlockEndX( 0 ),
202
0
    nBlockStartY( 0 ),
203
0
    nBlockStartYOrig( 0 ),
204
0
    nBlockEndY( 0 ),
205
0
    nBlockStartZ( 0 ),
206
0
    nBlockEndZ( 0 ),
207
0
    nOldCurX( 0 ),
208
0
    nOldCurY( 0 ),
209
0
    mfPendingTabBarWidth( -1.0 ),
210
0
    mnLOKStartHeaderRow( -2 ),
211
0
    mnLOKEndHeaderRow( -1 ),
212
0
    mnLOKStartHeaderCol( -2 ),
213
0
    mnLOKEndHeaderCol( -1 ),
214
0
    bMinimized( false ),
215
0
    bInUpdateHeader( false ),
216
0
    bInActivatePart( false ),
217
0
    bInZoomUpdate( false ),
218
0
    bMoveIsShift( false ),
219
0
    bDrawSelMode( false ),
220
0
    bLockPaintBrush( false ),
221
0
    bDragging( false ),
222
0
    bBlockNeg( false ),
223
0
    bBlockCols( false ),
224
0
    bBlockRows( false ),
225
0
    mbInlineWithScrollbar( false )
226
0
{
227
    // copy settings of existing shell for this document
228
0
    if (ScTabViewShell* pExistingViewShell = rDocSh.GetBestViewShell())
229
0
    {
230
0
        aViewRenderingData = pExistingViewShell->GetViewRenderingData();
231
0
        EnableAutoSpell(pExistingViewShell->IsAutoSpell());
232
0
    }
233
0
    Init();
234
0
}
235
236
void ScTabView::InitScrollBar(ScrollAdaptor& rScrollBar, tools::Long nMaxVal, const Link<weld::Scrollbar&, void>& rLink)
237
0
{
238
0
    rScrollBar.SetRange( Range( 0, nMaxVal ) );
239
0
    rScrollBar.SetLineSize( 1 );
240
0
    rScrollBar.SetPageSize( 1 );                // is queried separately
241
0
    rScrollBar.SetVisibleSize( 10 );            // is reset by Resize
242
243
0
    rScrollBar.SetScrollHdl(rLink);
244
0
    rScrollBar.SetMouseReleaseHdl(LINK(this, ScTabView, EndScrollHdl));
245
246
0
    rScrollBar.EnableRTL( aViewData.GetDocument().IsLayoutRTL( aViewData.CurrentTabForData() ) );
247
248
    // Related: tdf#155266 Eliminate delayed scrollbar redrawing when swiping
249
    // By default, the layout idle timer in the InterimWindowItem class
250
    // is set to TaskPriority::RESIZE. That is too high of a priority as
251
    // it appears that other timers are drawing after the scrollbar has been
252
    // redrawn.
253
    // As a result, when swiping, the content moves fluidly but the scrollbar
254
    // thumb does not move until after swiping stops or pauses. Then, after a
255
    // short lag, the scrollbar thumb finally "jumps" to the expected
256
    // position.
257
    // So, to fix this scrollbar "stickiness" when swiping, setting the
258
    // priority to TaskPriority::POST_PAINT causes the scrollbar to be
259
    // redrawn after any competing timers.
260
0
    rScrollBar.SetPriority(TaskPriority::POST_PAINT);
261
0
}
262
263
//  Scroll-Timer
264
void ScTabView::SetTimer( ScGridWindow* pWin, const MouseEvent& rMEvt )
265
0
{
266
0
    pTimerWindow = pWin;
267
0
    aTimerMEvt   = rMEvt;
268
0
    aScrollTimer.Start();
269
0
}
270
271
void ScTabView::ResetTimer()
272
0
{
273
0
    aScrollTimer.Stop();
274
0
    pTimerWindow = nullptr;
275
0
}
276
277
IMPL_LINK_NOARG(ScTabView, TimerHdl, Timer *, void)
278
0
{
279
0
    if (pTimerWindow)
280
0
        pTimerWindow->MouseMove( aTimerMEvt );
281
0
}
282
283
// --- Resize ---------------------------------------------------------------------
284
285
static void lcl_SetPosSize( vcl::Window& rWindow, const Point& rPos, const Size& rSize,
286
                            tools::Long nTotalWidth, bool bLayoutRTL )
287
0
{
288
0
    Point aNewPos = rPos;
289
0
    if ( bLayoutRTL )
290
0
    {
291
0
        aNewPos.setX( nTotalWidth - rPos.X() - rSize.Width() );
292
0
        if ( aNewPos == rWindow.GetPosPixel() && rSize.Width() != rWindow.GetSizePixel().Width() )
293
0
        {
294
            //  Document windows are manually painted right-to-left, so they need to
295
            //  be repainted if the size changes.
296
0
            rWindow.Invalidate();
297
0
        }
298
0
    }
299
0
    rWindow.SetPosSizePixel( aNewPos, rSize );
300
0
}
301
302
void ScTabView::DoResize( const Point& rOffset, const Size& rSize, bool bInner )
303
0
{
304
0
    HideListBox();
305
306
0
    bool bHasHint = HasHintWindow();
307
0
    if (bHasHint)
308
0
        RemoveHintWindow();
309
310
0
    bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.CurrentTabForData() );
311
0
    tools::Long nTotalWidth = rSize.Width();
312
0
    if ( bLayoutRTL )
313
0
        nTotalWidth += 2*rOffset.X();
314
315
0
    bool bVScroll    = aViewData.IsVScrollMode();
316
0
    bool bHScroll    = aViewData.IsHScrollMode();
317
0
    bool bTabControl = aViewData.IsTabMode();
318
0
    bool bHeaders    = aViewData.IsHeaderMode();
319
0
    bool bOutlMode   = aViewData.IsOutlineMode();
320
0
    bool bHOutline   = bOutlMode && lcl_HasColOutline(aViewData);
321
0
    bool bVOutline   = bOutlMode && lcl_HasRowOutline(aViewData);
322
323
0
    if ( aViewData.GetDocShell()->IsPreview() )
324
0
        bHScroll = bVScroll = bTabControl = bHeaders = bHOutline = bVOutline = false;
325
326
0
    tools::Long nBarX = 0;
327
0
    tools::Long nBarY = 0;
328
0
    tools::Long nOutlineX = 0;
329
0
    tools::Long nOutlineY = 0;
330
0
    tools::Long nOutPosX;
331
0
    tools::Long nOutPosY;
332
333
0
    tools::Long nPosX = rOffset.X();
334
0
    tools::Long nPosY = rOffset.Y();
335
0
    tools::Long nSizeX = rSize.Width();
336
0
    tools::Long nSizeY = rSize.Height();
337
338
0
    bMinimized = ( nSizeX<=SC_ICONSIZE || nSizeY<=SC_ICONSIZE );
339
0
    if ( bMinimized )
340
0
        return;
341
342
0
    float fScaleFactor = pFrameWin->GetDPIScaleFactor();
343
344
0
    tools::Long nSplitSizeX = SPLIT_HANDLE_SIZE * fScaleFactor;
345
0
    if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
346
0
        nSplitSizeX = 1;
347
0
    tools::Long nSplitSizeY = SPLIT_HANDLE_SIZE * fScaleFactor;
348
0
    if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
349
0
        nSplitSizeY = 1;
350
351
0
    aBorderPos = rOffset;
352
0
    aFrameSize = rSize;
353
354
0
    const StyleSettings& rStyleSettings = pFrameWin->GetSettings().GetStyleSettings();
355
356
357
0
    Size aFontSize = rStyleSettings.GetTabFont().GetFontSize();
358
0
#ifndef MACOSX // already in pixels on macOS: a point conversion would inflate the tab bar
359
0
    MapMode aPtMapMode(MapUnit::MapPoint);
360
0
    aFontSize = pFrameWin->LogicToPixel(aFontSize, aPtMapMode);
361
0
#endif
362
0
    sal_Int32 nTabHeight = aFontSize.Height() + TAB_HEIGHT_MARGIN;
363
364
0
    if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
365
0
    {
366
0
        if ( aViewData.GetHSplitPos() > nSizeX - SPLIT_MARGIN )
367
0
        {
368
0
            aViewData.SetHSplitMode( SC_SPLIT_NONE );
369
0
            if ( WhichH( aViewData.GetActivePart() ) == SC_SPLIT_RIGHT )
370
0
                ActivatePart( SC_SPLIT_BOTTOMLEFT );
371
0
            InvalidateSplit();
372
0
        }
373
0
    }
374
0
    if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
375
0
    {
376
0
        if ( aViewData.GetVSplitPos() > nSizeY - SPLIT_MARGIN )
377
0
        {
378
0
            aViewData.SetVSplitMode( SC_SPLIT_NONE );
379
0
            if ( WhichV( aViewData.GetActivePart() ) == SC_SPLIT_TOP )
380
0
                ActivatePart( SC_SPLIT_BOTTOMLEFT );
381
0
            InvalidateSplit();
382
0
        }
383
0
    }
384
385
0
    UpdateShow();
386
387
0
    if (bHScroll || bVScroll)       // Scrollbars horizontal or vertical
388
0
    {
389
0
        tools::Long nScrollBarSize = rStyleSettings.GetScrollBarSize();
390
0
        if (bVScroll)
391
0
        {
392
0
            nBarX = nScrollBarSize;
393
0
            nSizeX -= nBarX;
394
0
        }
395
0
        if (bHScroll)
396
0
        {
397
0
            nBarY = nTabHeight;
398
399
0
            if (!mbInlineWithScrollbar)
400
0
                nBarY += nScrollBarSize;
401
402
0
            nSizeY -= nBarY;
403
404
0
            tools::Long nSizeLt = 0;       // left scroll bar
405
0
            tools::Long nSizeRt = 0;       // right scroll bar
406
0
            tools::Long nSizeSp = 0;       // splitter
407
408
0
            switch (aViewData.GetHSplitMode())
409
0
            {
410
0
                case SC_SPLIT_NONE:
411
0
                    nSizeSp = nSplitSizeX;
412
0
                    nSizeLt = nSizeX - nSizeSp; // Convert the corner
413
0
                    break;
414
0
                case SC_SPLIT_NORMAL:
415
0
                    nSizeSp = nSplitSizeX;
416
0
                    nSizeLt = aViewData.GetHSplitPos();
417
0
                    break;
418
0
                case SC_SPLIT_FIX:
419
0
                    nSizeSp = 0;
420
0
                    nSizeLt = 0;
421
0
                    break;
422
0
            }
423
0
            nSizeRt = nSizeX - nSizeLt - nSizeSp;
424
425
0
            tools::Long nTabSize = 0;
426
427
0
            if (bTabControl)
428
0
            {
429
                // pending relative tab bar width from extended document options
430
0
                if( mfPendingTabBarWidth >= 0.0 )
431
0
                {
432
0
                    SetRelTabBarWidth( mfPendingTabBarWidth );
433
0
                    mfPendingTabBarWidth = -1.0;
434
0
                }
435
436
0
                if (mbInlineWithScrollbar)
437
0
                {
438
0
                    nTabSize = pTabControl->GetSizePixel().Width();
439
440
0
                    if ( aViewData.GetHSplitMode() != SC_SPLIT_FIX ) // left Scrollbar
441
0
                    {
442
0
                        if (nTabSize > nSizeLt-SC_SCROLLBAR_MIN)
443
0
                            nTabSize = nSizeLt-SC_SCROLLBAR_MIN;
444
0
                        if (nTabSize < SC_TABBAR_MIN)
445
0
                            nTabSize = SC_TABBAR_MIN;
446
0
                        nSizeLt -= nTabSize;
447
0
                    }
448
0
                    else // right Scrollbar
449
0
                    {
450
0
                        if (nTabSize > nSizeRt-SC_SCROLLBAR_MIN)
451
0
                            nTabSize = nSizeRt-SC_SCROLLBAR_MIN;
452
0
                        if (nTabSize < SC_TABBAR_MIN)
453
0
                            nTabSize = SC_TABBAR_MIN;
454
0
                        nSizeRt -= nTabSize;
455
0
                    }
456
0
                }
457
0
            }
458
459
0
            if (mbInlineWithScrollbar)
460
0
            {
461
0
                Point aTabPoint(nPosX, nPosY + nSizeY);
462
0
                Size aTabSize(nTabSize, nBarY);
463
0
                lcl_SetPosSize(*pTabControl, aTabPoint, aTabSize, nTotalWidth, bLayoutRTL);
464
0
                pTabControl->SetSheetLayoutRTL(bLayoutRTL);
465
466
0
                Point aHScrollLeftPoint(nPosX + nTabSize, nPosY + nSizeY);
467
0
                Size aHScrollLeftSize(nSizeLt, nBarY);
468
0
                lcl_SetPosSize(*aHScrollLeft, aHScrollLeftPoint, aHScrollLeftSize, nTotalWidth, bLayoutRTL);
469
470
0
                Point aHSplitterPoint(nPosX + nTabSize + nSizeLt, nPosY + nSizeY);
471
0
                Size aHSplitterSize(nSizeSp, nBarY);
472
0
                lcl_SetPosSize(*pHSplitter, aHSplitterPoint, aHSplitterSize, nTotalWidth, bLayoutRTL);
473
474
0
                Point aHScrollRightPoint(nPosX + nTabSize + nSizeLt + nSizeSp, nPosY + nSizeY);
475
0
                Size aHScrollRightSize(nSizeRt, nBarY);
476
0
                lcl_SetPosSize(*aHScrollRight, aHScrollRightPoint, aHScrollRightSize, nTotalWidth, bLayoutRTL);
477
0
            }
478
0
            else
479
0
            {
480
0
                Point aTabPoint(nPosX, nPosY + nSizeY + nScrollBarSize);
481
0
                Size aTabSize(nSizeX, nTabHeight);
482
0
                lcl_SetPosSize(*pTabControl, aTabPoint, aTabSize, nTotalWidth, bLayoutRTL);
483
0
                pTabControl->SetSheetLayoutRTL(bLayoutRTL);
484
485
0
                Point aHScrollLeftPoint(nPosX, nPosY + nSizeY);
486
0
                Size aHScrollLeftSize(nSizeLt, nScrollBarSize);
487
0
                lcl_SetPosSize(*aHScrollLeft, aHScrollLeftPoint, aHScrollLeftSize, nTotalWidth, bLayoutRTL);
488
489
0
                Point aHSplitterPoint(nPosX + nSizeLt, nPosY + nSizeY);
490
0
                Size aHSplitterSize(nSizeSp, nScrollBarSize);
491
0
                lcl_SetPosSize(*pHSplitter, aHSplitterPoint, aHSplitterSize, nTotalWidth, bLayoutRTL);
492
493
0
                Point aHScrollRightPoint(nPosX + nSizeLt + nSizeSp, nPosY + nSizeY);
494
0
                Size aHScrollRightSize(nSizeRt, nScrollBarSize);
495
0
                lcl_SetPosSize(*aHScrollRight, aHScrollRightPoint, aHScrollRightSize, nTotalWidth, bLayoutRTL);
496
0
            }
497
            //  SetDragRectPixel is done below
498
0
        }
499
500
0
        if (bVScroll)
501
0
        {
502
0
            tools::Long nSizeUp = 0;       // upper scroll bar
503
0
            tools::Long nSizeSp = 0;       // splitter
504
0
            tools::Long nSizeDn;           // lower scroll bar
505
506
0
            switch (aViewData.GetVSplitMode())
507
0
            {
508
0
                case SC_SPLIT_NONE:
509
0
                    nSizeUp = 0;
510
0
                    nSizeSp = nSplitSizeY;
511
0
                    break;
512
0
                case SC_SPLIT_NORMAL:
513
0
                    nSizeUp = aViewData.GetVSplitPos();
514
0
                    nSizeSp = nSplitSizeY;
515
0
                    break;
516
0
                case SC_SPLIT_FIX:
517
0
                    nSizeUp = 0;
518
0
                    nSizeSp = 0;
519
0
                    break;
520
0
            }
521
0
            nSizeDn = nSizeY - nSizeUp - nSizeSp;
522
523
0
            lcl_SetPosSize( *aVScrollTop, Point(nPosX + nSizeX, nPosY),
524
0
                                            Size(nBarX, nSizeUp), nTotalWidth, bLayoutRTL );
525
0
            lcl_SetPosSize( *pVSplitter, Point( nPosX + nSizeX, nPosY+nSizeUp ),
526
0
                                            Size( nBarX, nSizeSp ), nTotalWidth, bLayoutRTL );
527
0
            lcl_SetPosSize( *aVScrollBottom, Point(nPosX + nSizeX,
528
0
                                                nPosY + nSizeUp + nSizeSp),
529
0
                                            Size(nBarX, nSizeDn), nTotalWidth, bLayoutRTL );
530
531
            //  SetDragRectPixel is done below
532
0
        }
533
0
    }
534
535
    //  SetDragRectPixel also without Scrollbars etc., when already split
536
0
    if ( bHScroll || aViewData.GetHSplitMode() != SC_SPLIT_NONE )
537
0
        pHSplitter->SetDragRectPixel(
538
0
            tools::Rectangle( nPosX, nPosY, nPosX+nSizeX, nPosY+nSizeY ), pFrameWin );
539
0
    if ( bVScroll || aViewData.GetVSplitMode() != SC_SPLIT_NONE )
540
0
        pVSplitter->SetDragRectPixel(
541
0
            tools::Rectangle( nPosX, nPosY, nPosX+nSizeX, nPosY+nSizeY ), pFrameWin );
542
543
0
    if (bTabControl && ! bHScroll )
544
0
    {
545
0
        nBarY = aHScrollLeft->GetSizePixel().Height();
546
547
0
        tools::Long nSize1 = nSizeX;
548
549
0
        tools::Long nTabSize = nSize1;
550
0
        if (nTabSize < 0) nTabSize = 0;
551
552
0
        lcl_SetPosSize( *pTabControl, Point(nPosX, nPosY+nSizeY-nBarY),
553
0
                                        Size(nTabSize, nBarY), nTotalWidth, bLayoutRTL );
554
0
        nSizeY -= nBarY;
555
556
0
        if( bVScroll )
557
0
        {
558
0
            Size aVScrSize = aVScrollBottom->GetSizePixel();
559
0
            aVScrSize.AdjustHeight( -nBarY );
560
0
            aVScrollBottom->SetSizePixel( aVScrSize );
561
0
        }
562
0
    }
563
564
0
    nOutPosX = nPosX;
565
0
    nOutPosY = nPosY;
566
567
    // Outline-Controls
568
0
    if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
569
0
    {
570
0
        nOutlineX = pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize();
571
0
        nSizeX -= nOutlineX;
572
0
        nPosX += nOutlineX;
573
0
    }
574
0
    if (bHOutline && pColOutline[SC_SPLIT_LEFT])
575
0
    {
576
0
        nOutlineY = pColOutline[SC_SPLIT_LEFT]->GetDepthSize();
577
0
        nSizeY -= nOutlineY;
578
0
        nPosY += nOutlineY;
579
0
    }
580
581
0
    if (bHeaders)                               // column/row header
582
0
    {
583
0
        nBarX = pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
584
0
        nBarY = pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
585
0
        nSizeX -= nBarX;
586
0
        nSizeY -= nBarY;
587
0
        nPosX += nBarX;
588
0
        nPosY += nBarY;
589
0
    }
590
0
    else
591
0
        nBarX = nBarY = 0;
592
593
        // evaluate splitter
594
595
0
    tools::Long nLeftSize   = nSizeX;
596
0
    tools::Long nRightSize  = 0;
597
0
    tools::Long nTopSize    = 0;
598
0
    tools::Long nBottomSize = nSizeY;
599
0
    tools::Long nSplitPosX  = nPosX;
600
0
    tools::Long nSplitPosY  = nPosY;
601
602
0
    if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
603
0
    {
604
0
        tools::Long nSplitHeight = rSize.Height();
605
0
        if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
606
0
        {
607
            // Do not allow freeze splitter to overlap scroll bar/tab bar
608
0
            if ( bHScroll )
609
0
                nSplitHeight -= aHScrollLeft->GetSizePixel().Height();
610
0
            else if ( bTabControl && pTabControl )
611
0
                nSplitHeight -= pTabControl->GetSizePixel().Height();
612
0
        }
613
0
        nSplitPosX = aViewData.GetHSplitPos();
614
0
        lcl_SetPosSize( *pHSplitter,
615
0
                        Point(nSplitPosX, nOutPosY),
616
0
                        Size(nSplitSizeX, nSplitHeight - nTabHeight), nTotalWidth, bLayoutRTL);
617
0
        nLeftSize = nSplitPosX - nPosX;
618
0
        nSplitPosX += nSplitSizeX;
619
0
        nRightSize = nSizeX - nLeftSize - nSplitSizeX;
620
0
    }
621
0
    if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
622
0
    {
623
0
        tools::Long nSplitWidth = rSize.Width();
624
0
        if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX && bVScroll )
625
0
            nSplitWidth -= aVScrollBottom->GetSizePixel().Width();
626
0
        nSplitPosY = aViewData.GetVSplitPos();
627
0
        lcl_SetPosSize( *pVSplitter,
628
0
            Point( nOutPosX, nSplitPosY ), Size( nSplitWidth, nSplitSizeY ), nTotalWidth, bLayoutRTL );
629
0
        nTopSize = nSplitPosY - nPosY;
630
0
        nSplitPosY += nSplitSizeY;
631
0
        nBottomSize = nSizeY - nTopSize - nSplitSizeY;
632
0
    }
633
634
    //  ShowHide for pColOutline / pRowOutline happens in UpdateShow
635
636
0
    if (bHOutline)                              // Outline-Controls
637
0
    {
638
0
        if (pColOutline[SC_SPLIT_LEFT])
639
0
        {
640
0
            pColOutline[SC_SPLIT_LEFT]->SetHeaderSize( nBarX );
641
0
            lcl_SetPosSize( *pColOutline[SC_SPLIT_LEFT],
642
0
                    Point(nPosX-nBarX,nOutPosY), Size(nLeftSize+nBarX,nOutlineY), nTotalWidth, bLayoutRTL );
643
0
        }
644
0
        if (pColOutline[SC_SPLIT_RIGHT])
645
0
        {
646
0
            pColOutline[SC_SPLIT_RIGHT]->SetHeaderSize( 0 );    // always call to update RTL flag
647
0
            lcl_SetPosSize( *pColOutline[SC_SPLIT_RIGHT],
648
0
                    Point(nSplitPosX,nOutPosY), Size(nRightSize,nOutlineY), nTotalWidth, bLayoutRTL );
649
0
        }
650
0
    }
651
0
    if (bVOutline)
652
0
    {
653
0
        if (nTopSize)
654
0
        {
655
0
            if (pRowOutline[SC_SPLIT_TOP] && pRowOutline[SC_SPLIT_BOTTOM])
656
0
            {
657
0
                pRowOutline[SC_SPLIT_TOP]->SetHeaderSize( nBarY );
658
0
                lcl_SetPosSize( *pRowOutline[SC_SPLIT_TOP],
659
0
                        Point(nOutPosX,nPosY-nBarY), Size(nOutlineX,nTopSize+nBarY), nTotalWidth, bLayoutRTL );
660
0
                pRowOutline[SC_SPLIT_BOTTOM]->SetHeaderSize( 0 );
661
0
                lcl_SetPosSize( *pRowOutline[SC_SPLIT_BOTTOM],
662
0
                        Point(nOutPosX,nSplitPosY), Size(nOutlineX,nBottomSize), nTotalWidth, bLayoutRTL );
663
0
            }
664
0
        }
665
0
        else if (pRowOutline[SC_SPLIT_BOTTOM])
666
0
        {
667
0
            pRowOutline[SC_SPLIT_BOTTOM]->SetHeaderSize( nBarY );
668
0
            lcl_SetPosSize( *pRowOutline[SC_SPLIT_BOTTOM],
669
0
                    Point(nOutPosX,nSplitPosY-nBarY), Size(nOutlineX,nBottomSize+nBarY), nTotalWidth, bLayoutRTL );
670
0
        }
671
0
    }
672
0
    if (bHOutline && bVOutline)
673
0
    {
674
0
        lcl_SetPosSize( *aTopButton, Point(nOutPosX,nOutPosY), Size(nOutlineX,nOutlineY), nTotalWidth, bLayoutRTL );
675
0
        aTopButton->Show();
676
0
    }
677
0
    else
678
0
        aTopButton->Hide();
679
680
0
    if (bHeaders)                               // column/row header
681
0
    {
682
0
        lcl_SetPosSize( *pColBar[SC_SPLIT_LEFT],
683
0
            Point(nPosX,nPosY-nBarY), Size(nLeftSize,nBarY), nTotalWidth, bLayoutRTL );
684
0
        if (pColBar[SC_SPLIT_RIGHT])
685
0
            lcl_SetPosSize( *pColBar[SC_SPLIT_RIGHT],
686
0
                Point(nSplitPosX,nPosY-nBarY), Size(nRightSize,nBarY), nTotalWidth, bLayoutRTL );
687
688
0
        if (pRowBar[SC_SPLIT_TOP])
689
0
            lcl_SetPosSize( *pRowBar[SC_SPLIT_TOP],
690
0
                Point(nPosX-nBarX,nPosY), Size(nBarX,nTopSize), nTotalWidth, bLayoutRTL );
691
0
        lcl_SetPosSize( *pRowBar[SC_SPLIT_BOTTOM],
692
0
            Point(nPosX-nBarX,nSplitPosY), Size(nBarX,nBottomSize), nTotalWidth, bLayoutRTL );
693
694
0
        lcl_SetPosSize( *aCornerButton, Point(nPosX-nBarX,nPosY-nBarY), Size(nBarX,nBarY), nTotalWidth, bLayoutRTL );
695
0
        aCornerButton->Show();
696
0
        pColBar[SC_SPLIT_LEFT]->Show();
697
0
        pRowBar[SC_SPLIT_BOTTOM]->Show();
698
0
    }
699
0
    else
700
0
    {
701
0
        aCornerButton->Hide();
702
0
        pColBar[SC_SPLIT_LEFT]->Hide();         // always here
703
0
        pRowBar[SC_SPLIT_BOTTOM]->Hide();
704
0
    }
705
706
                                            // Grid-Windows
707
708
0
    if (bInner)
709
0
    {
710
0
        tools::Long nInnerPosX = bLayoutRTL ? ( nTotalWidth - nPosX - nLeftSize ) : nPosX;
711
0
        pGridWin[SC_SPLIT_BOTTOMLEFT]->SetPosPixel( Point(nInnerPosX,nSplitPosY) );
712
0
    }
713
0
    else
714
0
    {
715
0
        lcl_SetPosSize( *pGridWin[SC_SPLIT_BOTTOMLEFT],
716
0
            Point(nPosX,nSplitPosY), Size(nLeftSize,nBottomSize), nTotalWidth, bLayoutRTL );
717
0
        if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
718
0
            lcl_SetPosSize( *pGridWin[SC_SPLIT_BOTTOMRIGHT],
719
0
                Point(nSplitPosX,nSplitPosY), Size(nRightSize,nBottomSize), nTotalWidth, bLayoutRTL );
720
0
        if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
721
0
            lcl_SetPosSize( *pGridWin[SC_SPLIT_TOPLEFT],
722
0
                Point(nPosX,nPosY), Size(nLeftSize,nTopSize), nTotalWidth, bLayoutRTL );
723
0
        if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE && aViewData.GetVSplitMode() != SC_SPLIT_NONE )
724
0
            lcl_SetPosSize( *pGridWin[SC_SPLIT_TOPRIGHT],
725
0
                Point(nSplitPosX,nPosY), Size(nRightSize,nTopSize), nTotalWidth, bLayoutRTL );
726
0
    }
727
728
                // update scroll bars
729
730
0
    if (!bInUpdateHeader)
731
0
    {
732
0
        UpdateScrollBars();     // don't reset scroll bars when scrolling
733
0
        UpdateHeaderWidth();
734
735
0
        InterpretVisible();     // have everything calculated before painting
736
0
    }
737
738
0
    if (bHasHint)
739
0
        TestHintWindow();       // reposition
740
741
0
    UpdateVarZoom();    //  update variable zoom types (after resizing GridWindows)
742
743
0
    if (aViewData.GetViewShell()->HasAccessibilityObjects())
744
0
        aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccWindowResized));
745
0
}
746
747
void ScTabView::UpdateVarZoom()
748
0
{
749
    //  update variable zoom types
750
751
0
    SvxZoomType eZoomType = GetZoomType();
752
0
    if (eZoomType == SvxZoomType::PERCENT || bInZoomUpdate)
753
0
        return;
754
755
0
    bInZoomUpdate = true;
756
0
    double fOldX = GetViewData().GetZoomX();
757
0
    double fOldY = GetViewData().GetZoomY();
758
0
    tools::Long nOldPercent = tools::Long(fOldY * 100);
759
0
    sal_uInt16 nNewZoom = CalcZoom( eZoomType, static_cast<sal_uInt16>(nOldPercent) );
760
0
    double fNew = double(nNewZoom) / 100;
761
762
0
    if ( fNew != fOldX || fNew != fOldY )
763
0
    {
764
0
        SetZoom( fNew, fNew, false );   // always separately per sheet
765
0
        PaintGrid();
766
0
        PaintTop();
767
0
        PaintLeft();
768
0
        aViewData.GetViewShell()->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOM );
769
0
        aViewData.GetViewShell()->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER );
770
0
        aViewData.GetBindings().Invalidate(SID_ZOOM_IN);
771
0
        aViewData.GetBindings().Invalidate(SID_ZOOM_OUT);
772
0
    }
773
0
    bInZoomUpdate = false;
774
0
}
775
776
void ScTabView::UpdateFixPos()
777
0
{
778
0
    bool bResize = false;
779
0
    if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
780
0
        if (aViewData.UpdateFixX())
781
0
            bResize = true;
782
0
    if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
783
0
        if (aViewData.UpdateFixY())
784
0
            bResize = true;
785
0
    if (bResize)
786
0
        RepeatResize(false);
787
0
}
788
789
void ScTabView::RepeatResize( bool bUpdateFix )
790
0
{
791
0
    if ( bUpdateFix )
792
0
    {
793
0
        ScSplitMode eHSplit = aViewData.GetHSplitMode();
794
0
        ScSplitMode eVSplit = aViewData.GetVSplitMode();
795
796
        // #i46796# UpdateFixX / UpdateFixY uses GetGridOffset, which requires the
797
        // outline windows to be available. So UpdateShow has to be called before
798
        // (also called from DoResize).
799
0
        if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX )
800
0
            UpdateShow();
801
802
0
        if ( eHSplit == SC_SPLIT_FIX )
803
0
            aViewData.UpdateFixX();
804
0
        if ( eVSplit == SC_SPLIT_FIX )
805
0
            aViewData.UpdateFixY();
806
0
    }
807
808
0
    DoResize( aBorderPos, aFrameSize );
809
810
    //! border must be reset ???
811
0
}
812
813
void ScTabView::GetBorderSize( SvBorder& rBorder, const Size& /* rSize */ )
814
0
{
815
0
    bool bScrollBars = aViewData.IsVScrollMode();
816
0
    bool bHeaders    = aViewData.IsHeaderMode();
817
0
    bool bOutlMode   = aViewData.IsOutlineMode();
818
0
    bool bHOutline   = bOutlMode && lcl_HasColOutline(aViewData);
819
0
    bool bVOutline   = bOutlMode && lcl_HasRowOutline(aViewData);
820
0
    bool bLayoutRTL  = aViewData.GetDocument().IsLayoutRTL( aViewData.CurrentTabForData() );
821
822
0
    rBorder = SvBorder();
823
824
0
    if (bScrollBars)                            // Scrollbars horizontal or vertical
825
0
    {
826
0
        rBorder.Right()  += aVScrollBottom->GetSizePixel().Width();
827
0
        rBorder.Bottom() += aHScrollLeft->GetSizePixel().Height();
828
0
    }
829
830
    // Outline-Controls
831
0
    if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
832
0
        rBorder.Left() += pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize();
833
0
    if (bHOutline && pColOutline[SC_SPLIT_LEFT])
834
0
        rBorder.Top()  += pColOutline[SC_SPLIT_LEFT]->GetDepthSize();
835
836
0
    if (bHeaders)                               // column/row headers
837
0
    {
838
0
        rBorder.Left() += pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
839
0
        rBorder.Top()  += pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
840
0
    }
841
842
0
    if ( bLayoutRTL )
843
0
        ::std::swap( rBorder.Left(), rBorder.Right() );
844
0
}
845
846
IMPL_LINK_NOARG(ScTabView, TabBarResize, TabBar*, void)
847
0
{
848
0
    if (!aViewData.IsHScrollMode())
849
0
        return;
850
851
0
    tools::Long nSize = pTabControl->GetSplitSize();
852
853
0
    if (aViewData.GetHSplitMode() != SC_SPLIT_FIX)
854
0
    {
855
0
        tools::Long nMax = pHSplitter->GetPosPixel().X();
856
0
        if( pTabControl->IsEffectiveRTL() )
857
0
            nMax = pFrameWin->GetSizePixel().Width() - nMax;
858
0
        --nMax;
859
0
        if (nSize>nMax) nSize = nMax;
860
0
    }
861
862
0
    if ( nSize != pTabControl->GetSizePixel().Width() )
863
0
    {
864
0
        pTabControl->SetSizePixel( Size( nSize,
865
0
                                    pTabControl->GetSizePixel().Height() ) );
866
0
        RepeatResize();
867
0
    }
868
0
}
869
870
void ScTabView::SetTabBarWidth( tools::Long nNewWidth )
871
0
{
872
0
    Size aSize = pTabControl->GetSizePixel();
873
874
0
    if ( aSize.Width() != nNewWidth )
875
0
    {
876
0
        aSize.setWidth( nNewWidth );
877
0
        pTabControl->SetSizePixel( aSize );
878
0
    }
879
0
}
880
881
void ScTabView::SetRelTabBarWidth( double fRelTabBarWidth )
882
0
{
883
0
    if( (0.0 <= fRelTabBarWidth) && (fRelTabBarWidth <= 1.0) )
884
0
        if( tools::Long nFrameWidth = pFrameWin->GetSizePixel().Width() )
885
0
            SetTabBarWidth( static_cast< tools::Long >( fRelTabBarWidth * nFrameWidth + 0.5 ) );
886
0
}
887
888
void ScTabView::SetPendingRelTabBarWidth( double fRelTabBarWidth )
889
0
{
890
0
    mfPendingTabBarWidth = fRelTabBarWidth;
891
0
    SetRelTabBarWidth( fRelTabBarWidth );
892
0
}
893
894
tools::Long ScTabView::GetTabBarWidth() const
895
0
{
896
0
    return pTabControl->GetSizePixel().Width();
897
0
}
898
899
double ScTabView::GetRelTabBarWidth()
900
0
{
901
0
    return 0.5;
902
0
}
903
904
ScGridWindow* ScTabView::GetActiveWin()
905
0
{
906
0
    ScSplitPos ePos = aViewData.GetActivePart();
907
0
    OSL_ENSURE(pGridWin[ePos],"no active window");
908
0
    return pGridWin[ePos];
909
0
}
910
911
void ScTabView::SetActivePointer( PointerStyle nPointer )
912
0
{
913
0
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
914
0
        if (pWin)
915
0
            pWin->SetPointer( nPointer );
916
0
}
917
918
void ScTabView::ActiveGrabFocus()
919
0
{
920
0
    ScSplitPos ePos = aViewData.GetActivePart();
921
0
    if (pGridWin[ePos])
922
0
        pGridWin[ePos]->GrabFocus();
923
0
}
924
925
ScSplitPos ScTabView::FindWindow( const vcl::Window* pWindow ) const
926
0
{
927
0
    ScSplitPos eVal = SC_SPLIT_BOTTOMLEFT;      // Default
928
0
    for (sal_uInt16 i=0; i<4; i++)
929
0
        if ( pGridWin[i] == pWindow )
930
0
            eVal = static_cast<ScSplitPos>(i);
931
932
0
    return eVal;
933
0
}
934
935
Point ScTabView::GetGridOffset() const
936
0
{
937
0
    Point aPos;
938
939
        // size as in DoResize
940
941
0
    bool bHeaders    = aViewData.IsHeaderMode();
942
0
    bool bOutlMode   = aViewData.IsOutlineMode();
943
0
    bool bHOutline   = bOutlMode && lcl_HasColOutline(aViewData);
944
0
    bool bVOutline   = bOutlMode && lcl_HasRowOutline(aViewData);
945
946
    // Outline-Controls
947
0
    if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
948
0
        aPos.AdjustX(pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize() );
949
0
    if (bHOutline && pColOutline[SC_SPLIT_LEFT])
950
0
        aPos.AdjustY(pColOutline[SC_SPLIT_LEFT]->GetDepthSize() );
951
952
0
    if (bHeaders)                               // column/row headers
953
0
    {
954
0
        if (pRowBar[SC_SPLIT_BOTTOM])
955
0
            aPos.AdjustX(pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() );
956
0
        if (pColBar[SC_SPLIT_LEFT])
957
0
            aPos.AdjustY(pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() );
958
0
    }
959
960
0
    return aPos;
961
0
}
962
963
// ---  Scroll-Bars  --------------------------------------------------------
964
965
void ScTabView::SetZoomPercentFromCommand(sal_uInt16 nZoomPercent)
966
0
{
967
    // scroll wheel doesn't set the AppOptions default
968
969
0
    bool bSyncZoom = ScModule::get()->GetAppOptions().GetSynchronizeZoom();
970
0
    SetZoomType(SvxZoomType::PERCENT, bSyncZoom);
971
0
    double fFract = double(nZoomPercent) / 100;
972
0
    SetZoom(fFract, fFract, bSyncZoom);
973
0
    PaintGrid();
974
0
    PaintTop();
975
0
    PaintLeft();
976
0
    aViewData.GetBindings().Invalidate( SID_ATTR_ZOOM);
977
0
    aViewData.GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER);
978
0
    aViewData.GetBindings().Invalidate( SID_ZOOM_IN);
979
0
    aViewData.GetBindings().Invalidate( SID_ZOOM_OUT);
980
0
}
981
982
bool ScTabView::ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos )
983
0
{
984
0
    HideNoteOverlay();
985
986
0
    bool bDone = false;
987
0
    const CommandWheelData* pData = rCEvt.GetWheelData();
988
0
    if (pData && pData->GetMode() == CommandWheelMode::ZOOM)
989
0
    {
990
0
        if ( !aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace() )
991
0
        {
992
            //  for ole inplace editing, the scale is defined by the visarea and client size
993
            //  and can't be changed directly
994
995
0
            double fOldY = aViewData.GetZoomY();
996
0
            sal_uInt16 nOld = static_cast<tools::Long>( fOldY * 100 );
997
0
            sal_uInt16 nNew;
998
0
            if ( pData->GetDelta() < 0 )
999
0
                nNew = std::max( MINZOOM, basegfx::zoomtools::zoomOut( nOld ));
1000
0
            else
1001
0
                nNew = std::min( MAXZOOM, basegfx::zoomtools::zoomIn( nOld ));
1002
0
            if ( nNew != nOld )
1003
0
            {
1004
0
                SetZoomPercentFromCommand(nNew);
1005
0
            }
1006
1007
0
            bDone = true;
1008
0
        }
1009
0
    }
1010
0
    else
1011
0
    {
1012
0
        ScHSplitPos eHPos = WhichH(ePos);
1013
0
        ScVSplitPos eVPos = WhichV(ePos);
1014
0
        ScrollAdaptor* pHScroll = ( eHPos == SC_SPLIT_LEFT ) ? aHScrollLeft.get() : aHScrollRight.get();
1015
0
        ScrollAdaptor* pVScroll = ( eVPos == SC_SPLIT_TOP )  ? aVScrollTop.get()  : aVScrollBottom.get();
1016
0
        if ( pGridWin[ePos] )
1017
0
            bDone = pGridWin[ePos]->HandleScrollCommand( rCEvt, pHScroll, pVScroll );
1018
0
    }
1019
0
    return bDone;
1020
0
}
1021
1022
bool ScTabView::GesturePanCommand(const CommandEvent& rCEvt)
1023
0
{
1024
0
    HideNoteOverlay();
1025
1026
0
    bool bDone = false;
1027
0
    const CommandGesturePanData* pData = rCEvt.GetGesturePanData();
1028
0
    if (!pData)
1029
0
        return false;
1030
1031
0
    if (aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace())
1032
0
        return false;
1033
1034
0
    ScSplitPos ePos = aViewData.GetActivePart();
1035
0
    ScHSplitPos eHPos = WhichH(ePos);
1036
0
    ScVSplitPos eVPos = WhichV(ePos);
1037
0
    ScrollAdaptor* pHScroll = (eHPos == SC_SPLIT_LEFT) ? aHScrollLeft.get() : aHScrollRight.get();
1038
0
    ScrollAdaptor* pVScroll = (eVPos == SC_SPLIT_TOP) ? aVScrollTop.get() : aVScrollBottom.get();
1039
0
    if (pGridWin[ePos])
1040
0
        bDone = pGridWin[ePos]->HandleScrollCommand(rCEvt, pHScroll, pVScroll);
1041
1042
0
    return bDone;
1043
0
}
1044
1045
bool ScTabView::GestureZoomCommand(const CommandEvent& rCEvt)
1046
0
{
1047
0
    HideNoteOverlay();
1048
1049
0
    const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData();
1050
0
    if (!pData)
1051
0
        return false;
1052
1053
0
    if (aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace())
1054
0
        return false;
1055
1056
0
    if (pData->meEventType == GestureEventZoomType::Begin)
1057
0
    {
1058
0
        mfLastZoomScale = pData->mfScaleDelta;
1059
0
        return true;
1060
0
    }
1061
1062
0
    if (pData->meEventType == GestureEventZoomType::Update)
1063
0
    {
1064
0
        double deltaBetweenEvents = (pData->mfScaleDelta - mfLastZoomScale) / mfLastZoomScale;
1065
0
        mfLastZoomScale = pData->mfScaleDelta;
1066
1067
        // Accumulate fractional zoom to avoid small zoom changes from being ignored
1068
0
        mfAccumulatedZoom += deltaBetweenEvents;
1069
0
        int nZoomChangePercent = mfAccumulatedZoom * 100;
1070
0
        mfAccumulatedZoom -= nZoomChangePercent / 100.0;
1071
1072
0
        double fOldY = aViewData.GetZoomY();
1073
0
        sal_uInt16 nOld = static_cast<tools::Long>(fOldY * 100);
1074
0
        sal_uInt16 nNew = nOld + nZoomChangePercent;
1075
0
        nNew = std::clamp<sal_uInt16>(nNew, MINZOOM, MAXZOOM);
1076
1077
0
        if (nNew != nOld)
1078
0
        {
1079
0
            SetZoomPercentFromCommand(nNew);
1080
0
        }
1081
1082
0
        return true;
1083
0
    }
1084
0
    return true;
1085
0
}
1086
1087
IMPL_LINK_NOARG(ScTabView, HScrollLeftHdl, weld::Scrollbar&, void)
1088
0
{
1089
0
    ScrollHdl(aHScrollLeft.get());
1090
0
}
1091
1092
IMPL_LINK_NOARG(ScTabView, HScrollRightHdl, weld::Scrollbar&, void)
1093
0
{
1094
0
    ScrollHdl(aHScrollRight.get());
1095
0
}
1096
1097
IMPL_LINK_NOARG(ScTabView, VScrollTopHdl, weld::Scrollbar&, void)
1098
0
{
1099
0
    ScrollHdl(aVScrollTop.get());
1100
0
}
1101
1102
IMPL_LINK_NOARG(ScTabView, VScrollBottomHdl, weld::Scrollbar&, void)
1103
0
{
1104
0
    ScrollHdl(aVScrollBottom.get());
1105
0
}
1106
1107
IMPL_LINK_NOARG(ScTabView, EndScrollHdl, const MouseEvent&, bool)
1108
0
{
1109
0
    if (bDragging)
1110
0
    {
1111
0
        UpdateScrollBars();
1112
0
        bDragging = false;
1113
0
    }
1114
0
    return false;
1115
0
}
1116
1117
void ScTabView::ScrollHdl(ScrollAdaptor* pScroll)
1118
0
{
1119
0
    bool bUpdateHorizontalScrollbars = false;
1120
1121
0
    bool bHoriz = ( pScroll == aHScrollLeft.get() || pScroll == aHScrollRight.get() );
1122
0
    tools::Long nViewPos;
1123
0
    if ( bHoriz )
1124
0
        nViewPos = aViewData.GetPosX( (pScroll == aHScrollLeft.get()) ?
1125
0
                                        SC_SPLIT_LEFT : SC_SPLIT_RIGHT );
1126
0
    else
1127
0
        nViewPos = aViewData.GetPosY( (pScroll == aVScrollTop.get()) ?
1128
0
                                        SC_SPLIT_TOP : SC_SPLIT_BOTTOM );
1129
1130
0
    bool bLayoutRTL = bHoriz && aViewData.GetDocument().IsLayoutRTL( aViewData.CurrentTabForData() );
1131
1132
0
    ScrollType eType = pScroll->GetScrollType();
1133
0
    if ( eType == ScrollType::Drag )
1134
0
    {
1135
0
        if (!bDragging)
1136
0
        {
1137
0
            bDragging = true;
1138
0
            nPrevDragPos = nViewPos;
1139
0
        }
1140
1141
        // show scroll position
1142
        // (only QuickHelp, there is no entry for it in the status bar)
1143
1144
0
        if (Help::IsQuickHelpEnabled())
1145
0
        {
1146
0
            Size aSize = pScroll->GetSizePixel();
1147
1148
            /*  Convert scrollbar mouse position to screen position. If RTL
1149
                mode of scrollbar differs from RTL mode of its parent, then the
1150
                direct call to Window::OutputToNormalizedScreenPixel() will
1151
                give unusable results, because calculation of screen position
1152
                is based on parent orientation and expects equal orientation of
1153
                the child position. Need to mirror mouse position before. */
1154
0
            Point aMousePos = pScroll->GetPointerPosPixel();
1155
0
            if( pScroll->IsRTLEnabled() != pScroll->GetParent()->IsRTLEnabled() )
1156
0
                aMousePos.setX( aSize.Width() - aMousePos.X() - 1 );
1157
0
            aMousePos = pScroll->OutputToNormalizedScreenPixel( aMousePos );
1158
1159
            // convert top-left position of scrollbar to screen position
1160
0
            Point aPos = pScroll->OutputToNormalizedScreenPixel( Point() );
1161
1162
            // get scrollbar scroll position for help text (row number/column name)
1163
0
            tools::Long nScrollMin = 0;        // simulate RangeMin
1164
0
            if ( aViewData.GetHSplitMode()==SC_SPLIT_FIX && pScroll == aHScrollRight.get() )
1165
0
                nScrollMin = aViewData.GetFixPosX();
1166
0
            if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() )
1167
0
                nScrollMin = aViewData.GetFixPosY();
1168
0
            tools::Long nScrollPos = GetScrollBarPos( *pScroll, bLayoutRTL ) + nScrollMin;
1169
1170
0
            OUString aHelpStr;
1171
0
            tools::Rectangle aRect;
1172
0
            QuickHelpFlags nAlign;
1173
0
            if (bHoriz)
1174
0
            {
1175
0
                aHelpStr = ScResId(STR_COLUMN) +
1176
0
                           " " + ScColToAlpha(static_cast<SCCOL>(nScrollPos));
1177
1178
0
                aRect.SetLeft( aMousePos.X() );
1179
0
                aRect.SetTop( aPos.Y() - 4 );
1180
0
                nAlign       = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
1181
0
            }
1182
0
            else
1183
0
            {
1184
0
                aHelpStr = ScResId(STR_ROW) +
1185
0
                           " " + OUString::number(nScrollPos + 1);
1186
1187
                // note that bLayoutRTL is always false here, because bLayoutRTL depends on bHoriz
1188
1189
                // show quicktext always inside sheet area
1190
0
                aRect.SetLeft(aPos.X() - 8);
1191
0
                aRect.SetTop( aMousePos.Y() );
1192
0
                nAlign = QuickHelpFlags::Right | QuickHelpFlags::VCenter;
1193
0
            }
1194
0
            aRect.SetRight( aRect.Left() );
1195
0
            aRect.SetBottom( aRect.Top() );
1196
1197
0
            Help::ShowQuickHelp(pScroll->GetParent(), aRect, aHelpStr, nAlign);
1198
0
        }
1199
0
    }
1200
0
    else
1201
0
        bDragging = false;
1202
1203
0
    if ( bHoriz && bLayoutRTL )
1204
0
    {
1205
        // change scroll type so visible/previous cells calculation below remains the same
1206
0
        switch ( eType )
1207
0
        {
1208
0
            case ScrollType::LineUp:   eType = ScrollType::LineDown; break;
1209
0
            case ScrollType::LineDown: eType = ScrollType::LineUp;   break;
1210
0
            case ScrollType::PageUp:   eType = ScrollType::PageDown; break;
1211
0
            case ScrollType::PageDown: eType = ScrollType::PageUp;   break;
1212
0
            default:
1213
0
            {
1214
                // added to avoid warnings
1215
0
            }
1216
0
        }
1217
0
    }
1218
1219
0
    tools::Long nDelta(0);
1220
0
    switch ( eType )
1221
0
    {
1222
0
        case ScrollType::LineUp:
1223
0
            nDelta = -1;
1224
0
            break;
1225
0
        case ScrollType::LineDown:
1226
0
            nDelta = 1;
1227
0
            break;
1228
0
        case ScrollType::PageUp:
1229
0
            if ( pScroll == aHScrollLeft.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsX( SC_SPLIT_LEFT ));
1230
0
            if ( pScroll == aHScrollRight.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsX( SC_SPLIT_RIGHT ));
1231
0
            if ( pScroll == aVScrollTop.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsY( SC_SPLIT_TOP ));
1232
0
            if ( pScroll == aVScrollBottom.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsY( SC_SPLIT_BOTTOM ));
1233
0
            if (nDelta==0) nDelta=-1;
1234
0
            break;
1235
0
        case ScrollType::PageDown:
1236
0
            if ( pScroll == aHScrollLeft.get() ) nDelta = aViewData.VisibleCellsX( SC_SPLIT_LEFT );
1237
0
            if ( pScroll == aHScrollRight.get() ) nDelta = aViewData.VisibleCellsX( SC_SPLIT_RIGHT );
1238
0
            if ( pScroll == aVScrollTop.get() ) nDelta = aViewData.VisibleCellsY( SC_SPLIT_TOP );
1239
0
            if ( pScroll == aVScrollBottom.get() ) nDelta = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM );
1240
0
            if (nDelta==0) nDelta=1;
1241
0
            break;
1242
0
        default:
1243
0
            {
1244
                // only scroll in the correct direction, do not jitter around hidden ranges
1245
0
                tools::Long nScrollMin = 0;        // simulate RangeMin
1246
0
                if ( aViewData.GetHSplitMode()==SC_SPLIT_FIX && pScroll == aHScrollRight.get() )
1247
0
                    nScrollMin = aViewData.GetFixPosX();
1248
0
                if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() )
1249
0
                    nScrollMin = aViewData.GetFixPosY();
1250
1251
0
                tools::Long nScrollPos = GetScrollBarPos( *pScroll, bLayoutRTL ) + nScrollMin;
1252
0
                nDelta = nScrollPos - nViewPos;
1253
1254
                // tdf#152406 Disable anti-jitter code for scroll wheel events
1255
                // After moving thousands of columns to the right via
1256
                // horizontal scroll wheel or trackpad swipe events, most
1257
                // vertical scroll wheel or trackpad swipe events will trigger
1258
                // the anti-jitter code because nScrollPos and nPrevDragPos
1259
                // will be equal and nDelta will be overridden and set to zero.
1260
                // So, only use the anti-jitter code for mouse drag events.
1261
0
                if ( eType == ScrollType::Drag )
1262
0
                {
1263
0
                    if ( nScrollPos > nPrevDragPos )
1264
0
                    {
1265
0
                        if (nDelta<0) nDelta=0;
1266
0
                    }
1267
0
                    else if ( nScrollPos < nPrevDragPos )
1268
0
                    {
1269
0
                        if (nDelta>0) nDelta=0;
1270
0
                    }
1271
0
                    else
1272
0
                        nDelta = 0;
1273
0
                }
1274
0
                else if ( bHoriz )
1275
0
                {
1276
                    // tdf#135478 Reduce sensitivity of horizontal scrollwheel
1277
                    // Problem: at least on macOS, swipe events are very
1278
                    // precise. So, when swiping at a slight angle off of
1279
                    // vertical, swipe events will include a small amount
1280
                    // of horizontal movement. Since horizontal swipe units
1281
                    // are measured in cell widths, these small amounts of
1282
                    // horizontal movement results in shifting many columns
1283
                    // to the right or left while swiping almost vertically.
1284
                    // So my hacky fix is to reduce the amount of horizontal
1285
                    // swipe events to roughly match the "visual distance"
1286
                    // of vertical swipe events.
1287
                    // The reduction factor is arbitrary but is set to
1288
                    // roughly the ratio of default cell width divided by
1289
                    // default cell height. This hacky fix isn't a perfect
1290
                    // fix, but hopefully it reduces the amount of
1291
                    // unexpected horizontal shifting while swiping
1292
                    // vertically to a tolerable amount for most users.
1293
                    // Note: the potential downside of doing this is that
1294
                    // some users might find horizontal swiping to be
1295
                    // slower than they are used to. If that becomes an
1296
                    // issue for enough users, the reduction factor may
1297
                    // need to be lowered to find a good balance point.
1298
#ifdef _WIN32
1299
                    static const tools::Long nHScrollReductionFactor = 3;
1300
#else
1301
0
                    static const tools::Long nHScrollReductionFactor = 8;
1302
0
#endif
1303
1304
                    // tdf#161945 increase sensitivity for negative horizontal deltas
1305
                    // A side effect of the anti-jitter code is that it tends
1306
                    // to reduce the sensitivity of horizontal scroll events
1307
                    // towards the first column. It is particularly noticeable
1308
                    // with horizontal scroll events from a scrollwheel when
1309
                    // the view position is in column B or C. This results in
1310
                    // a very small delta from the anti-jitter code.
1311
                    // So, to balance things out, apply a constant adjustment
1312
                    // factor to increase the sensitivity more for small deltas
1313
                    // than for large deltas.
1314
0
                    static const tools::Long nHScrollNegativeDeltaAdjustmentFactor = -1;
1315
0
                    if ( nDelta < 0 )
1316
0
                        nDelta += nHScrollNegativeDeltaAdjustmentFactor;
1317
1318
0
                    if ( pScroll == aHScrollLeft.get() )
1319
0
                    {
1320
0
                        mnPendingaHScrollLeftDelta += nDelta;
1321
0
                        nDelta = 0;
1322
0
                        if ( abs(mnPendingaHScrollLeftDelta) > nHScrollReductionFactor )
1323
0
                        {
1324
0
                            nDelta = mnPendingaHScrollLeftDelta / nHScrollReductionFactor;
1325
0
                            mnPendingaHScrollLeftDelta = mnPendingaHScrollLeftDelta % nHScrollReductionFactor;
1326
0
                        }
1327
0
                    }
1328
0
                    else
1329
0
                    {
1330
0
                        mnPendingaHScrollRightDelta += nDelta;
1331
0
                        nDelta = 0;
1332
0
                        if ( abs(mnPendingaHScrollRightDelta) > nHScrollReductionFactor )
1333
0
                        {
1334
0
                            nDelta = mnPendingaHScrollRightDelta / nHScrollReductionFactor;
1335
0
                            mnPendingaHScrollRightDelta = mnPendingaHScrollRightDelta % nHScrollReductionFactor;
1336
0
                        }
1337
0
                    }
1338
1339
0
                    bUpdateHorizontalScrollbars = true;
1340
0
                }
1341
1342
0
                nPrevDragPos = nScrollPos;
1343
0
            }
1344
0
            break;
1345
0
    }
1346
1347
0
    if (nDelta)
1348
0
    {
1349
0
        bool bUpdate = ( eType != ScrollType::Drag );    // don't alter the ranges while dragging
1350
0
        if ( bHoriz )
1351
0
            ScrollX( nDelta, (pScroll == aHScrollLeft.get()) ? SC_SPLIT_LEFT : SC_SPLIT_RIGHT, bUpdate );
1352
0
        else
1353
0
            ScrollY( nDelta, (pScroll == aVScrollTop.get()) ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM, bUpdate );
1354
0
    }
1355
1356
    // tdf#161945 update horizontal scrollbar position to match tab view
1357
    // While the fix for tdf#135478 reduces the horizontal scroll delta
1358
    // applied to the tab view, the horizontal scrollbar is set before
1359
    // that with the original delta. As a result, the horizontal scrollbar
1360
    // is now mispositioned so update the horizontal scrollbar position
1361
    // to match the position of the tab view's visible columns.
1362
0
    if ( bUpdateHorizontalScrollbars )
1363
0
        UpdateScrollBars( COLUMN_HEADER );
1364
0
}
1365
1366
void ScTabView::ScrollX( tools::Long nDeltaX, ScHSplitPos eWhich, bool bUpdBars )
1367
0
{
1368
0
    ScDocument& rDoc = aViewData.GetDocument();
1369
0
    SCCOL nOldX = aViewData.GetPosX(eWhich);
1370
0
    SCCOL nNewX = nOldX + static_cast<SCCOL>(nDeltaX);
1371
0
    if ( nNewX < 0 )
1372
0
    {
1373
0
        nDeltaX -= nNewX;
1374
0
        nNewX = 0;
1375
0
    }
1376
0
    if ( nNewX > rDoc.MaxCol() )
1377
0
    {
1378
0
        nDeltaX -= nNewX - rDoc.MaxCol();
1379
0
        nNewX = rDoc.MaxCol();
1380
0
    }
1381
1382
0
    SCCOL nDir = ( nDeltaX > 0 ) ? 1 : -1;
1383
0
    SCTAB nTab = aViewData.CurrentTabForData();
1384
0
    while ( rDoc.ColHidden(nNewX, nTab) &&
1385
0
            nNewX+nDir >= 0 && nNewX+nDir <= rDoc.MaxCol() )
1386
0
        nNewX = sal::static_int_cast<SCCOL>( nNewX + nDir );
1387
1388
    // freeze
1389
1390
0
    if (aViewData.GetHSplitMode() == SC_SPLIT_FIX)
1391
0
    {
1392
0
        if (eWhich == SC_SPLIT_LEFT)
1393
0
            nNewX = nOldX;          // always keep the left part
1394
0
        else
1395
0
        {
1396
0
            SCCOL nFixX = aViewData.GetFixPosX();
1397
0
            if (nNewX < nFixX)
1398
0
                nNewX = nFixX;
1399
0
        }
1400
0
    }
1401
0
    if (nNewX == nOldX)
1402
0
        return;
1403
1404
0
    HideAllCursors();
1405
1406
0
    if ( nNewX >= 0 && nNewX <= rDoc.MaxCol() && nDeltaX )
1407
0
    {
1408
0
        SCCOL nTrackX = std::max( nOldX, nNewX );
1409
1410
        // with VCL Update() affects all windows at the moment, that is why
1411
        // calling Update after scrolling of the GridWindow would possibly
1412
        // already have painted the column/row bar with updated position.  -
1413
        // Therefore call Update once before on column/row bar
1414
0
        if (pColBar[eWhich])
1415
0
            pColBar[eWhich]->PaintImmediately();
1416
1417
0
        tools::Long nOldPos = aViewData.GetScrPos( nTrackX, 0, eWhich ).X();
1418
0
        aViewData.SetPosX( eWhich, nNewX );
1419
0
        tools::Long nDiff = aViewData.GetScrPos( nTrackX, 0, eWhich ).X() - nOldPos;
1420
1421
0
        if ( eWhich==SC_SPLIT_LEFT )
1422
0
        {
1423
0
            pGridWin[SC_SPLIT_BOTTOMLEFT]->ScrollPixel( nDiff, 0 );
1424
0
            if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
1425
0
                pGridWin[SC_SPLIT_TOPLEFT]->ScrollPixel( nDiff, 0 );
1426
0
        }
1427
0
        else
1428
0
        {
1429
0
            pGridWin[SC_SPLIT_BOTTOMRIGHT]->ScrollPixel( nDiff, 0 );
1430
0
            if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
1431
0
                pGridWin[SC_SPLIT_TOPRIGHT]->ScrollPixel( nDiff, 0 );
1432
0
        }
1433
0
        if (pColBar[eWhich])     { pColBar[eWhich]->Scroll( nDiff,0 ); pColBar[eWhich]->PaintImmediately(); }
1434
0
        if (pColOutline[eWhich]) pColOutline[eWhich]->ScrollPixel( nDiff );
1435
0
        if (bUpdBars)
1436
0
            UpdateScrollBars();
1437
0
    }
1438
1439
0
    if (nDeltaX==1 || nDeltaX==-1)
1440
0
        pGridWin[aViewData.GetActivePart()]->PaintImmediately();
1441
1442
0
    ShowAllCursors();
1443
1444
0
    SetNewVisArea();            // MapMode must already be set
1445
1446
0
    TestHintWindow();
1447
0
}
1448
1449
void ScTabView::ScrollY( tools::Long nDeltaY, ScVSplitPos eWhich, bool bUpdBars )
1450
0
{
1451
0
    ScDocument& rDoc = aViewData.GetDocument();
1452
0
    SCROW nOldY = aViewData.GetPosY(eWhich);
1453
0
    SCROW nNewY = nOldY + static_cast<SCROW>(nDeltaY);
1454
0
    if ( nNewY < 0 )
1455
0
    {
1456
0
        nDeltaY -= nNewY;
1457
0
        nNewY = 0;
1458
0
    }
1459
0
    if ( nNewY > rDoc.MaxRow() )
1460
0
    {
1461
0
        nDeltaY -= nNewY - rDoc.MaxRow();
1462
0
        nNewY = rDoc.MaxRow();
1463
0
    }
1464
1465
0
    SCROW nDir = ( nDeltaY > 0 ) ? 1 : -1;
1466
0
    SCTAB nTab = aViewData.CurrentTabForData();
1467
0
    while ( rDoc.RowHidden(nNewY, nTab) &&
1468
0
            nNewY+nDir >= 0 && nNewY+nDir <= rDoc.MaxRow() )
1469
0
        nNewY += nDir;
1470
1471
    // freeze
1472
1473
0
    if (aViewData.GetVSplitMode() == SC_SPLIT_FIX)
1474
0
    {
1475
0
        if (eWhich == SC_SPLIT_TOP)
1476
0
            nNewY = nOldY;                 // always keep the upper part
1477
0
        else
1478
0
        {
1479
0
            SCROW nFixY = aViewData.GetFixPosY();
1480
0
            if (nNewY < nFixY)
1481
0
                nNewY = nFixY;
1482
0
        }
1483
0
    }
1484
0
    if (nNewY == nOldY)
1485
0
        return;
1486
1487
0
    HideAllCursors();
1488
1489
0
    if ( nNewY >= 0 && nNewY <= rDoc.MaxRow() && nDeltaY )
1490
0
    {
1491
0
        SCROW nTrackY = std::max( nOldY, nNewY );
1492
1493
        // adjust row headers before the actual scrolling, so it does not get painted twice
1494
        // PosY may then also not be set yet, pass on new value
1495
0
        SCROW nUNew = nNewY;
1496
0
        UpdateHeaderWidth( &eWhich, &nUNew );               // adjust row headers
1497
1498
0
        if (pRowBar[eWhich])
1499
0
            pRowBar[eWhich]->PaintImmediately();
1500
1501
0
        tools::Long nOldPos = aViewData.GetScrPos( 0, nTrackY, eWhich ).Y();
1502
0
        aViewData.SetPosY( eWhich, nNewY );
1503
0
        tools::Long nDiff = aViewData.GetScrPos( 0, nTrackY, eWhich ).Y() - nOldPos;
1504
1505
0
        if ( eWhich==SC_SPLIT_TOP )
1506
0
        {
1507
0
            pGridWin[SC_SPLIT_TOPLEFT]->ScrollPixel( 0, nDiff );
1508
0
            if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
1509
0
                pGridWin[SC_SPLIT_TOPRIGHT]->ScrollPixel( 0, nDiff );
1510
0
        }
1511
0
        else
1512
0
        {
1513
0
            pGridWin[SC_SPLIT_BOTTOMLEFT]->ScrollPixel( 0, nDiff );
1514
0
            if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
1515
0
                pGridWin[SC_SPLIT_BOTTOMRIGHT]->ScrollPixel( 0, nDiff );
1516
0
        }
1517
0
        if (pRowBar[eWhich])     { pRowBar[eWhich]->Scroll( 0,nDiff ); pRowBar[eWhich]->PaintImmediately(); }
1518
0
        if (pRowOutline[eWhich]) pRowOutline[eWhich]->ScrollPixel( nDiff );
1519
0
        if (bUpdBars)
1520
0
            UpdateScrollBars();
1521
0
    }
1522
1523
0
    if (nDeltaY==1 || nDeltaY==-1)
1524
0
        pGridWin[aViewData.GetActivePart()]->PaintImmediately();
1525
1526
0
    ShowAllCursors();
1527
1528
0
    SetNewVisArea();            // MapMode must already be set
1529
1530
0
    TestHintWindow();
1531
0
}
1532
1533
void ScTabView::ScrollLines( tools::Long nDeltaX, tools::Long nDeltaY )
1534
0
{
1535
0
    ScSplitPos eWhich = aViewData.GetActivePart();
1536
0
    if (nDeltaX)
1537
0
        ScrollX(nDeltaX,WhichH(eWhich));
1538
0
    if (nDeltaY)
1539
0
        ScrollY(nDeltaY,WhichV(eWhich));
1540
0
}
1541
1542
namespace
1543
{
1544
1545
SCROW lcl_LastVisible( const ScViewData& rViewData )
1546
0
{
1547
    // If many rows are hidden at end of the document,
1548
    // then there should not be a switch to wide row headers because of this
1549
0
    ScDocument& rDoc = rViewData.GetDocument();
1550
0
    SCTAB nTab = rViewData.CurrentTabForData();
1551
1552
0
    SCROW nVis = rDoc.MaxRow();
1553
0
    SCROW startRow;
1554
0
    while ( nVis > 0 && rDoc.GetRowHeight( nVis, nTab, &startRow, nullptr ) == 0 )
1555
0
        nVis = std::max<SCROW>( startRow - 1, 0 );
1556
0
    return nVis;
1557
0
}
1558
1559
} // anonymous namespace
1560
1561
void ScTabView::UpdateHeaderWidth( const ScVSplitPos* pWhich, const SCROW* pPosY )
1562
0
{
1563
0
    if (!pRowBar[SC_SPLIT_BOTTOM])
1564
0
        return;
1565
1566
0
    ScDocument& rDoc = aViewData.GetDocument();
1567
0
    SCROW nEndPos = rDoc.MaxRow();
1568
0
    if ( !aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace() )
1569
0
    {
1570
        //  for OLE Inplace always MAXROW
1571
1572
0
        if ( pWhich && *pWhich == SC_SPLIT_BOTTOM && pPosY )
1573
0
            nEndPos = *pPosY;
1574
0
        else
1575
0
            nEndPos = aViewData.GetPosY( SC_SPLIT_BOTTOM );
1576
0
        nEndPos += aViewData.CellsAtY( nEndPos, 1, SC_SPLIT_BOTTOM ); // VisibleCellsY
1577
0
        if (nEndPos > rDoc.MaxRow())
1578
0
            nEndPos = lcl_LastVisible( aViewData );
1579
1580
0
        if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
1581
0
        {
1582
0
            SCROW nTopEnd;
1583
0
            if ( pWhich && *pWhich == SC_SPLIT_TOP && pPosY )
1584
0
                nTopEnd = *pPosY;
1585
0
            else
1586
0
                nTopEnd = aViewData.GetPosY( SC_SPLIT_TOP );
1587
0
            nTopEnd += aViewData.CellsAtY( nTopEnd, 1, SC_SPLIT_TOP );// VisibleCellsY
1588
0
            if (nTopEnd > rDoc.MaxRow())
1589
0
                nTopEnd = lcl_LastVisible( aViewData );
1590
1591
0
            if ( nTopEnd > nEndPos )
1592
0
                nEndPos = nTopEnd;
1593
0
        }
1594
0
    }
1595
1596
    // tdf#148507 - update row header width only on digit changes
1597
0
    tools::Long nWidth = pRowBar[SC_SPLIT_BOTTOM]->GetSmallWidth();
1598
0
    if (nEndPos >= 999999)
1599
0
        nWidth = pRowBar[SC_SPLIT_BOTTOM]->GetBigWidth();
1600
0
    else if (nEndPos >= 99999)
1601
0
        nWidth = pRowBar[SC_SPLIT_BOTTOM]->GetLargeWidth();
1602
0
    else if (nEndPos >= 9999)
1603
0
        nWidth = pRowBar[SC_SPLIT_BOTTOM]->GetMedWidth();
1604
1605
0
    if (nWidth == pRowBar[SC_SPLIT_BOTTOM]->GetWidth() || bInUpdateHeader)
1606
0
        return;
1607
1608
0
    bInUpdateHeader = true;
1609
1610
0
    pRowBar[SC_SPLIT_BOTTOM]->SetWidth( nWidth );
1611
0
    if (pRowBar[SC_SPLIT_TOP])
1612
0
        pRowBar[SC_SPLIT_TOP]->SetWidth( nWidth );
1613
1614
0
    RepeatResize();
1615
1616
    // on VCL there are endless updates (each Update is valid for all windows)
1617
    //aCornerButton->Update();       // otherwise this never gets an Update
1618
1619
0
    bInUpdateHeader = false;
1620
0
}
1621
1622
static void ShowHide( vcl::Window* pWin, bool bShow )
1623
0
{
1624
0
    OSL_ENSURE(pWin || !bShow, "window is not present");
1625
0
    if (pWin)
1626
0
        pWin->Show(bShow);
1627
0
}
1628
1629
void ScTabView::UpdateShow()
1630
0
{
1631
0
    bool bHScrollMode = aViewData.IsHScrollMode();
1632
0
    bool bVScrollMode = aViewData.IsVScrollMode();
1633
0
    bool bTabMode     = aViewData.IsTabMode();
1634
0
    bool bOutlMode    = aViewData.IsOutlineMode();
1635
0
    bool bHOutline    = bOutlMode && lcl_HasColOutline(aViewData);
1636
0
    bool bVOutline    = bOutlMode && lcl_HasRowOutline(aViewData);
1637
0
    bool bHeader      = aViewData.IsHeaderMode();
1638
1639
0
    bool bShowH = ( aViewData.GetHSplitMode() != SC_SPLIT_NONE );
1640
0
    bool bShowV = ( aViewData.GetVSplitMode() != SC_SPLIT_NONE );
1641
1642
0
    if ( aViewData.GetDocShell()->IsPreview() )
1643
0
        bHScrollMode = bVScrollMode = bTabMode = bHeader = bHOutline = bVOutline = false;
1644
1645
        // create Windows
1646
1647
0
    if (bShowH && !pGridWin[SC_SPLIT_BOTTOMRIGHT])
1648
0
    {
1649
0
        pGridWin[SC_SPLIT_BOTTOMRIGHT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_BOTTOMRIGHT );
1650
0
        DoAddWin( pGridWin[SC_SPLIT_BOTTOMRIGHT] );
1651
0
    }
1652
0
    if (bShowV && !pGridWin[SC_SPLIT_TOPLEFT])
1653
0
    {
1654
0
        pGridWin[SC_SPLIT_TOPLEFT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_TOPLEFT );
1655
0
        DoAddWin( pGridWin[SC_SPLIT_TOPLEFT] );
1656
0
    }
1657
0
    if (bShowH && bShowV && !pGridWin[SC_SPLIT_TOPRIGHT])
1658
0
    {
1659
0
        pGridWin[SC_SPLIT_TOPRIGHT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_TOPRIGHT );
1660
0
        DoAddWin( pGridWin[SC_SPLIT_TOPRIGHT] );
1661
0
    }
1662
1663
0
    if (bHOutline && !pColOutline[SC_SPLIT_LEFT])
1664
0
        pColOutline[SC_SPLIT_LEFT] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_HOR, aViewData, SC_SPLIT_BOTTOMLEFT );
1665
0
    if (bShowH && bHOutline && !pColOutline[SC_SPLIT_RIGHT])
1666
0
        pColOutline[SC_SPLIT_RIGHT] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_HOR, aViewData, SC_SPLIT_BOTTOMRIGHT );
1667
1668
0
    if (bVOutline && !pRowOutline[SC_SPLIT_BOTTOM])
1669
0
        pRowOutline[SC_SPLIT_BOTTOM] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_VER, aViewData, SC_SPLIT_BOTTOMLEFT );
1670
0
    if (bShowV && bVOutline && !pRowOutline[SC_SPLIT_TOP])
1671
0
        pRowOutline[SC_SPLIT_TOP] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_VER, aViewData, SC_SPLIT_TOPLEFT );
1672
1673
0
    if (bShowH && bHeader && !pColBar[SC_SPLIT_RIGHT])
1674
0
        pColBar[SC_SPLIT_RIGHT] = VclPtr<ScColBar>::Create( pFrameWin, SC_SPLIT_RIGHT,
1675
0
                                                            &aHdrFunc, pHdrSelEng.get(), this );
1676
0
    if (bShowV && bHeader && !pRowBar[SC_SPLIT_TOP])
1677
0
        pRowBar[SC_SPLIT_TOP] = VclPtr<ScRowBar>::Create( pFrameWin, SC_SPLIT_TOP,
1678
0
                                                          &aHdrFunc, pHdrSelEng.get(), this );
1679
1680
        // show Windows
1681
1682
0
    ShowHide( aHScrollLeft.get(), bHScrollMode );
1683
0
    ShowHide( aHScrollRight.get(), bShowH && bHScrollMode );
1684
0
    ShowHide( aVScrollBottom.get(), bVScrollMode );
1685
0
    ShowHide( aVScrollTop.get(), bShowV && bVScrollMode );
1686
1687
0
    ShowHide( pHSplitter, bHScrollMode || bShowH );         // always generated
1688
0
    ShowHide( pVSplitter, bVScrollMode || bShowV );
1689
0
    ShowHide( pTabControl, bTabMode );
1690
1691
                                                    // from here dynamically generated
1692
1693
0
    ShowHide( pGridWin[SC_SPLIT_BOTTOMRIGHT], bShowH );
1694
0
    ShowHide( pGridWin[SC_SPLIT_TOPLEFT], bShowV );
1695
0
    ShowHide( pGridWin[SC_SPLIT_TOPRIGHT], bShowH && bShowV );
1696
1697
0
    ShowHide( pColOutline[SC_SPLIT_LEFT], bHOutline );
1698
0
    ShowHide( pColOutline[SC_SPLIT_RIGHT], bShowH && bHOutline );
1699
1700
0
    ShowHide( pRowOutline[SC_SPLIT_BOTTOM], bVOutline );
1701
0
    ShowHide( pRowOutline[SC_SPLIT_TOP], bShowV && bVOutline );
1702
1703
0
    ShowHide( pColBar[SC_SPLIT_RIGHT], bShowH && bHeader );
1704
0
    ShowHide( pRowBar[SC_SPLIT_TOP], bShowV && bHeader );
1705
1706
    //! register new Gridwindows
1707
0
}
1708
1709
bool ScTabView::UpdateVisibleRange()
1710
0
{
1711
0
    bool bChanged = false;
1712
0
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
1713
0
    {
1714
0
        if (!pWin || !pWin->IsVisible())
1715
0
            continue;
1716
1717
0
        if (pWin->UpdateVisibleRange())
1718
0
            bChanged = true;
1719
0
    }
1720
1721
0
    return bChanged;
1722
0
}
1723
1724
// ---  Splitter  --------------------------------------------------------
1725
1726
IMPL_LINK( ScTabView, SplitHdl, Splitter*, pSplitter, void )
1727
0
{
1728
0
    if ( pSplitter == pHSplitter )
1729
0
        DoHSplit( pHSplitter->GetSplitPosPixel() );
1730
0
    else
1731
0
        DoVSplit( pVSplitter->GetSplitPosPixel() );
1732
1733
0
    if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX || aViewData.GetVSplitMode() == SC_SPLIT_FIX )
1734
0
        FreezeSplitters( true );
1735
1736
0
    DoResize( aBorderPos, aFrameSize );
1737
0
}
1738
1739
void ScTabView::DoHSplit(tools::Long nSplitPos)
1740
0
{
1741
    //  nSplitPos is the real pixel position on the frame window,
1742
    //  mirroring for RTL has to be done here.
1743
1744
0
    bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.CurrentTabForData() );
1745
0
    if ( bLayoutRTL )
1746
0
        nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
1747
1748
0
    tools::Long nMinPos;
1749
0
    tools::Long nMaxPos;
1750
1751
0
    nMinPos = SPLIT_MARGIN;
1752
0
    if ( pRowBar[SC_SPLIT_BOTTOM] && pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() >= nMinPos )
1753
0
        nMinPos = pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() + 1;
1754
0
    nMaxPos = aFrameSize.Width() - SPLIT_MARGIN;
1755
1756
0
    ScSplitMode aOldMode = aViewData.GetHSplitMode();
1757
0
    ScSplitMode aNewMode = SC_SPLIT_NORMAL;
1758
1759
0
    aViewData.SetHSplitPos( nSplitPos );
1760
0
    if ( nSplitPos < nMinPos || nSplitPos > nMaxPos )
1761
0
        aNewMode = SC_SPLIT_NONE;
1762
1763
0
    aViewData.SetHSplitMode( aNewMode );
1764
1765
0
    if ( aNewMode == aOldMode )
1766
0
        return;
1767
1768
0
    UpdateShow();       // before ActivatePart !!
1769
1770
0
    if ( aNewMode == SC_SPLIT_NONE )
1771
0
    {
1772
0
        if (aViewData.GetActivePart() == SC_SPLIT_TOPRIGHT)
1773
0
            ActivatePart( SC_SPLIT_TOPLEFT );
1774
0
        if (aViewData.GetActivePart() == SC_SPLIT_BOTTOMRIGHT)
1775
0
            ActivatePart( SC_SPLIT_BOTTOMLEFT );
1776
0
    }
1777
0
    else
1778
0
    {
1779
0
        SCCOL nOldDelta = aViewData.GetPosX( SC_SPLIT_LEFT );
1780
0
        tools::Long nLeftWidth = nSplitPos - pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
1781
0
        if ( nLeftWidth < 0 ) nLeftWidth = 0;
1782
0
        SCCOL nNewDelta = nOldDelta + aViewData.CellsAtX( nOldDelta, 1, SC_SPLIT_LEFT, nLeftWidth );
1783
1784
0
        ScDocument& rDoc = aViewData.GetDocument();
1785
0
        if ( nNewDelta > rDoc.MaxCol() )
1786
0
            nNewDelta = rDoc.MaxCol();
1787
0
        aViewData.SetPosX( SC_SPLIT_RIGHT, nNewDelta );
1788
0
        if ( nNewDelta > aViewData.GetCurX() )
1789
0
            ActivatePart( (WhichV(aViewData.GetActivePart()) == SC_SPLIT_BOTTOM) ?
1790
0
                SC_SPLIT_BOTTOMLEFT : SC_SPLIT_TOPLEFT );
1791
0
        else
1792
0
            ActivatePart( (WhichV(aViewData.GetActivePart()) == SC_SPLIT_BOTTOM) ?
1793
0
                SC_SPLIT_BOTTOMRIGHT : SC_SPLIT_TOPRIGHT );
1794
0
    }
1795
1796
    // Form Layer needs to know the visible part of all windows
1797
    // that is why MapMode must already be correct here
1798
0
    SyncGridWindowMapModeFromDrawMapMode();
1799
0
    SetNewVisArea();
1800
1801
0
    PaintGrid();
1802
0
    PaintTop();
1803
1804
0
    InvalidateSplit();
1805
0
}
1806
1807
void ScTabView::DoVSplit(tools::Long nSplitPos)
1808
0
{
1809
0
    tools::Long nMinPos;
1810
0
    tools::Long nMaxPos;
1811
0
    SCROW nOldDelta;
1812
1813
0
    nMinPos = SPLIT_MARGIN;
1814
0
    if ( pColBar[SC_SPLIT_LEFT] && pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() >= nMinPos )
1815
0
        nMinPos = pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() + 1;
1816
0
    nMaxPos = aFrameSize.Height() - SPLIT_MARGIN;
1817
1818
0
    ScSplitMode aOldMode = aViewData.GetVSplitMode();
1819
0
    ScSplitMode aNewMode = SC_SPLIT_NORMAL;
1820
1821
0
    aViewData.SetVSplitPos( nSplitPos );
1822
0
    if ( nSplitPos < nMinPos || nSplitPos > nMaxPos )
1823
0
        aNewMode = SC_SPLIT_NONE;
1824
1825
0
    aViewData.SetVSplitMode( aNewMode );
1826
1827
0
    if ( aNewMode == aOldMode )
1828
0
        return;
1829
1830
0
    UpdateShow();       // before ActivatePart !!
1831
1832
0
    if ( aNewMode == SC_SPLIT_NONE )
1833
0
    {
1834
0
        nOldDelta = aViewData.GetPosY( SC_SPLIT_TOP );
1835
0
        aViewData.SetPosY( SC_SPLIT_BOTTOM, nOldDelta );
1836
1837
0
        if (aViewData.GetActivePart() == SC_SPLIT_TOPLEFT)
1838
0
            ActivatePart( SC_SPLIT_BOTTOMLEFT );
1839
0
        if (aViewData.GetActivePart() == SC_SPLIT_TOPRIGHT)
1840
0
            ActivatePart( SC_SPLIT_BOTTOMRIGHT );
1841
0
    }
1842
0
    else
1843
0
    {
1844
0
        if ( aOldMode == SC_SPLIT_NONE )
1845
0
            nOldDelta = aViewData.GetPosY( SC_SPLIT_BOTTOM );
1846
0
        else
1847
0
            nOldDelta = aViewData.GetPosY( SC_SPLIT_TOP );
1848
1849
0
        aViewData.SetPosY( SC_SPLIT_TOP, nOldDelta );
1850
0
        tools::Long nTopHeight = nSplitPos - pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
1851
0
        if ( nTopHeight < 0 ) nTopHeight = 0;
1852
0
        SCROW nNewDelta = nOldDelta + aViewData.CellsAtY( nOldDelta, 1, SC_SPLIT_TOP, nTopHeight );
1853
1854
0
        ScDocument& rDoc = aViewData.GetDocument();
1855
0
        if ( nNewDelta > rDoc.MaxRow() )
1856
0
            nNewDelta = rDoc.MaxRow();
1857
0
        aViewData.SetPosY( SC_SPLIT_BOTTOM, nNewDelta );
1858
0
        if ( nNewDelta > aViewData.GetCurY() )
1859
0
            ActivatePart( (WhichH(aViewData.GetActivePart()) == SC_SPLIT_LEFT) ?
1860
0
                SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
1861
0
        else
1862
0
            ActivatePart( (WhichH(aViewData.GetActivePart()) == SC_SPLIT_LEFT) ?
1863
0
                SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
1864
0
    }
1865
1866
    // Form Layer needs to know the visible part of all windows
1867
    // that is why MapMode must already be correct here
1868
0
    SyncGridWindowMapModeFromDrawMapMode();
1869
0
    SetNewVisArea();
1870
1871
0
    PaintGrid();
1872
0
    PaintLeft();
1873
1874
0
    InvalidateSplit();
1875
0
}
1876
1877
Point ScTabView::GetInsertPos() const
1878
0
{
1879
0
    ScDocument& rDoc = aViewData.GetDocument();
1880
0
    SCCOL nCol = aViewData.GetCurX();
1881
0
    SCROW nRow = aViewData.GetCurY();
1882
0
    SCTAB nTab = aViewData.CurrentTabForData();
1883
0
    tools::Long nPosX = 0;
1884
0
    for (SCCOL i=0; i<nCol; i++)
1885
0
        nPosX += rDoc.GetColWidth(i,nTab);
1886
0
    nPosX = o3tl::convert(nPosX, o3tl::Length::twip, o3tl::Length::mm100);
1887
0
    if ( rDoc.IsNegativePage( nTab ) )
1888
0
        nPosX = -nPosX;
1889
0
    tools::Long nPosY = rDoc.GetRowHeight( 0, nRow-1, nTab);
1890
0
    nPosY = o3tl::convert(nPosY, o3tl::Length::twip, o3tl::Length::mm100);
1891
0
    return Point(nPosX,nPosY);
1892
0
}
1893
1894
Point ScTabView::GetChartInsertPos( const Size& rSize, const ScRange& rCellRange )
1895
0
{
1896
0
    Point aInsertPos;
1897
0
    const tools::Long nBorder = 100;   // leave 1mm for border
1898
0
    tools::Long nNeededWidth = rSize.Width() + 2 * nBorder;
1899
0
    tools::Long nNeededHeight = rSize.Height() + 2 * nBorder;
1900
1901
    // use the active window, or lower/right if frozen (as in CalcZoom)
1902
0
    ScSplitPos eUsedPart = aViewData.GetActivePart();
1903
0
    if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
1904
0
        eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
1905
0
    if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
1906
0
        eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
1907
1908
0
    ScGridWindow* pWin = pGridWin[eUsedPart].get();
1909
0
    OSL_ENSURE( pWin, "Window not found" );
1910
0
    if (pWin)
1911
0
    {
1912
0
        ActivatePart( eUsedPart );
1913
1914
        //  get the visible rectangle in logic units
1915
0
        bool bLOKActive = comphelper::LibreOfficeKit::isActive();
1916
0
        MapMode aDrawMode = pWin->GetDrawMapMode();
1917
0
        tools::Rectangle aVisible(
1918
0
            bLOKActive ?
1919
0
            OutputDevice::LogicToLogic( aViewData.getLOKVisibleArea(), MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM) )
1920
0
            : pWin->PixelToLogic( tools::Rectangle( Point(0,0), pWin->GetOutputSizePixel() ), aDrawMode ) );
1921
1922
0
        ScDocument& rDoc = aViewData.GetDocument();
1923
0
        SCTAB nTab = aViewData.CurrentTabForData();
1924
0
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1925
0
        tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1926
1927
0
        tools::Long nDocX = o3tl::convert(rDoc.GetColOffset(rDoc.MaxCol() + 1, nTab), o3tl::Length::twip, o3tl::Length::mm100) * nLayoutSign;
1928
0
        tools::Long nDocY = o3tl::convert(rDoc.GetRowOffset( rDoc.MaxRow() + 1, nTab ), o3tl::Length::twip, o3tl::Length::mm100);
1929
1930
0
        if ( aVisible.Left() * nLayoutSign > nDocX * nLayoutSign )
1931
0
            aVisible.SetLeft( nDocX );
1932
0
        if ( aVisible.Right() * nLayoutSign > nDocX * nLayoutSign )
1933
0
            aVisible.SetRight( nDocX );
1934
0
        if ( aVisible.Top() > nDocY )
1935
0
            aVisible.SetTop( nDocY );
1936
0
        if ( aVisible.Bottom() > nDocY )
1937
0
            aVisible.SetBottom( nDocY );
1938
1939
        //  get the logic position of the selection
1940
1941
0
        tools::Rectangle aSelection = rDoc.GetMMRect( rCellRange.aStart.Col(), rCellRange.aStart.Row(),
1942
0
                                                rCellRange.aEnd.Col(), rCellRange.aEnd.Row(), nTab );
1943
1944
0
        if (bLOKActive && bLayoutRTL)
1945
0
        {
1946
            // In this case we operate in negative X coordinates. The rectangle aSelection already
1947
            // has negative X coordinates. So the x coordinates in the rectangle aVisible(from getLOKVisibleArea)
1948
            // need be negated to match.
1949
0
            aVisible = tools::Rectangle(-aVisible.Right(), aVisible.Top(), -aVisible.Left(), aVisible.Bottom());
1950
0
        }
1951
1952
0
        tools::Long nLeftSpace = aSelection.Left() - aVisible.Left();
1953
0
        tools::Long nRightSpace = aVisible.Right() - aSelection.Right();
1954
0
        tools::Long nTopSpace = aSelection.Top() - aVisible.Top();
1955
0
        tools::Long nBottomSpace = aVisible.Bottom() - aSelection.Bottom();
1956
1957
0
        bool bFitLeft = ( nLeftSpace >= nNeededWidth );
1958
0
        bool bFitRight = ( nRightSpace >= nNeededWidth );
1959
1960
0
        if ( bFitLeft || bFitRight )
1961
0
        {
1962
            // first preference: completely left or right of the selection
1963
1964
            // if both fit, prefer left in RTL mode, right otherwise
1965
0
            bool bPutLeft = bFitLeft && ( bLayoutRTL || !bFitRight );
1966
1967
0
            if ( bPutLeft )
1968
0
                aInsertPos.setX( aSelection.Left() - nNeededWidth );
1969
0
            else
1970
0
                aInsertPos.setX( aSelection.Right() + 1 );
1971
1972
            // align with top of selection (is moved again if it doesn't fit)
1973
0
            aInsertPos.setY( std::max( aSelection.Top(), aVisible.Top() ) );
1974
0
        }
1975
0
        else if ( nTopSpace >= nNeededHeight || nBottomSpace >= nNeededHeight )
1976
0
        {
1977
            // second preference: completely above or below the selection
1978
0
            if ( nBottomSpace > nNeededHeight )             // bottom is preferred
1979
0
                aInsertPos.setY( aSelection.Bottom() + 1 );
1980
0
            else
1981
0
                aInsertPos.setY( aSelection.Top() - nNeededHeight );
1982
1983
            // align with (logic) left edge of selection (moved again if it doesn't fit)
1984
0
            if ( bLayoutRTL )
1985
0
                aInsertPos.setX( std::min( aSelection.Right(), aVisible.Right() ) - nNeededWidth + 1 );
1986
0
            else
1987
0
                aInsertPos.setX( std::max( aSelection.Left(), aVisible.Left() ) );
1988
0
        }
1989
0
        else
1990
0
        {
1991
            // place to the (logic) right of the selection and move so it fits
1992
1993
0
            if ( bLayoutRTL )
1994
0
                aInsertPos.setX( aSelection.Left() - nNeededWidth );
1995
0
            else
1996
0
                aInsertPos.setX( aSelection.Right() + 1 );
1997
0
            aInsertPos.setY( std::max( aSelection.Top(), aVisible.Top() ) );
1998
0
        }
1999
2000
        // move the position if the object doesn't fit in the screen
2001
2002
0
        tools::Rectangle aCompareRect( aInsertPos, Size( nNeededWidth, nNeededHeight ) );
2003
0
        if ( aCompareRect.Right() > aVisible.Right() )
2004
0
            aInsertPos.AdjustX( -(aCompareRect.Right() - aVisible.Right()) );
2005
0
        if ( aCompareRect.Bottom() > aVisible.Bottom() )
2006
0
            aInsertPos.AdjustY( -(aCompareRect.Bottom() - aVisible.Bottom()) );
2007
2008
0
        if ( aInsertPos.X() < aVisible.Left() )
2009
0
            aInsertPos.setX( aVisible.Left() );
2010
0
        if ( aInsertPos.Y() < aVisible.Top() )
2011
0
            aInsertPos.setY( aVisible.Top() );
2012
2013
        // nNeededWidth / nNeededHeight includes all borders - move aInsertPos to the
2014
        // object position, inside the border
2015
2016
0
        aInsertPos.AdjustX(nBorder );
2017
0
        aInsertPos.AdjustY(nBorder );
2018
0
    }
2019
0
    return aInsertPos;
2020
0
}
2021
2022
Point ScTabView::GetChartDialogPos( const Size& rDialogSize, const tools::Rectangle& rLogicChart )
2023
0
{
2024
    // rDialogSize must be in pixels, rLogicChart in 1/100 mm. Return value is in pixels.
2025
2026
0
    Point aRet;
2027
2028
    // use the active window, or lower/right if frozen (as in CalcZoom)
2029
0
    ScSplitPos eUsedPart = aViewData.GetActivePart();
2030
0
    if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
2031
0
        eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
2032
0
    if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
2033
0
        eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
2034
2035
0
    ScGridWindow* pWin = pGridWin[eUsedPart].get();
2036
0
    OSL_ENSURE( pWin, "Window not found" );
2037
0
    if (pWin)
2038
0
    {
2039
0
        MapMode aDrawMode = pWin->GetDrawMapMode();
2040
0
        tools::Rectangle aObjPixel = pWin->LogicToPixel( rLogicChart, aDrawMode );
2041
0
        AbsoluteScreenPixelRectangle aObjAbs( pWin->OutputToAbsoluteScreenPixel( aObjPixel.TopLeft() ),
2042
0
                           pWin->OutputToAbsoluteScreenPixel( aObjPixel.BottomRight() ) );
2043
2044
0
        AbsoluteScreenPixelRectangle aDesktop = pWin->GetDesktopRectPixel();
2045
0
        Size aSpace = pWin->LogicToPixel( Size(8, 12), MapMode(MapUnit::MapAppFont));
2046
2047
0
        ScDocument& rDoc = aViewData.GetDocument();
2048
0
        SCTAB nTab = aViewData.CurrentTabForData();
2049
0
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
2050
2051
0
        bool bCenterHor = false;
2052
2053
0
        if ( aDesktop.Bottom() - aObjAbs.Bottom() >= rDialogSize.Height() + aSpace.Height() )
2054
0
        {
2055
            // first preference: below the chart
2056
2057
0
            aRet.setY( aObjAbs.Bottom() + aSpace.Height() );
2058
0
            bCenterHor = true;
2059
0
        }
2060
0
        else if ( aObjAbs.Top() - aDesktop.Top() >= rDialogSize.Height() + aSpace.Height() )
2061
0
        {
2062
            // second preference: above the chart
2063
2064
0
            aRet.setY( aObjAbs.Top() - rDialogSize.Height() - aSpace.Height() );
2065
0
            bCenterHor = true;
2066
0
        }
2067
0
        else
2068
0
        {
2069
0
            bool bFitLeft = ( aObjAbs.Left() - aDesktop.Left() >= rDialogSize.Width() + aSpace.Width() );
2070
0
            bool bFitRight = ( aDesktop.Right() - aObjAbs.Right() >= rDialogSize.Width() + aSpace.Width() );
2071
2072
0
            if ( bFitLeft || bFitRight )
2073
0
            {
2074
                // if both fit, prefer right in RTL mode, left otherwise
2075
0
                bool bPutRight = bFitRight && ( bLayoutRTL || !bFitLeft );
2076
0
                if ( bPutRight )
2077
0
                    aRet.setX( aObjAbs.Right() + aSpace.Width() );
2078
0
                else
2079
0
                    aRet.setX( aObjAbs.Left() - rDialogSize.Width() - aSpace.Width() );
2080
2081
                // center vertically
2082
0
                aRet.setY( aObjAbs.Top() + ( aObjAbs.GetHeight() - rDialogSize.Height() ) / 2 );
2083
0
            }
2084
0
            else
2085
0
            {
2086
                // doesn't fit on any edge - put at the bottom of the screen
2087
0
                aRet.setY( aDesktop.Bottom() - rDialogSize.Height() );
2088
0
                bCenterHor = true;
2089
0
            }
2090
0
        }
2091
0
        if ( bCenterHor )
2092
0
            aRet.setX( aObjAbs.Left() + ( aObjAbs.GetWidth() - rDialogSize.Width() ) / 2 );
2093
2094
        // limit to screen (centering might lead to invalid positions)
2095
0
        if ( aRet.X() + rDialogSize.Width() - 1 > aDesktop.Right() )
2096
0
            aRet.setX( aDesktop.Right() - rDialogSize.Width() + 1 );
2097
0
        if ( aRet.X() < aDesktop.Left() )
2098
0
            aRet.setX( aDesktop.Left() );
2099
0
        if ( aRet.Y() + rDialogSize.Height() - 1 > aDesktop.Bottom() )
2100
0
            aRet.setY( aDesktop.Bottom() - rDialogSize.Height() + 1 );
2101
0
        if ( aRet.Y() < aDesktop.Top() )
2102
0
            aRet.setY( aDesktop.Top() );
2103
0
    }
2104
2105
0
    return aRet;
2106
0
}
2107
2108
void ScTabView::LockModifiers( sal_uInt16 nModifiers )
2109
0
{
2110
0
    pSelEngine->LockModifiers( nModifiers );
2111
0
    pHdrSelEng->LockModifiers( nModifiers );
2112
0
}
2113
2114
sal_uInt16 ScTabView::GetLockedModifiers() const
2115
0
{
2116
0
    return pSelEngine->GetLockedModifiers();
2117
0
}
2118
2119
Point ScTabView::GetMousePosPixel()
2120
0
{
2121
0
    Point aPos;
2122
0
    ScGridWindow* pWin = GetActiveWin();
2123
2124
0
    if ( pWin )
2125
0
        aPos = pWin->GetMousePosPixel();
2126
2127
0
    return aPos;
2128
0
}
2129
2130
void ScTabView::FreezeSplitters( bool bFreeze, SplitMethod eSplitMethod, SCCOLROW nFreezeIndex)
2131
0
{
2132
0
    if ((eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_ROW) && nFreezeIndex < 0)
2133
0
        nFreezeIndex = 0;
2134
2135
0
    ScSplitMode eOldH = aViewData.GetHSplitMode();
2136
0
    ScSplitMode eOldV = aViewData.GetVSplitMode();
2137
2138
0
    ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
2139
0
    if ( eOldV != SC_SPLIT_NONE )
2140
0
        ePos = SC_SPLIT_TOPLEFT;
2141
0
    vcl::Window* pWin = pGridWin[ePos];
2142
2143
0
    bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.CurrentTabForData() );
2144
0
    bool bUpdateFix = false;
2145
2146
0
    if ( bFreeze )
2147
0
    {
2148
0
        Point aWinStart = pWin->GetPosPixel();
2149
0
        aViewData.GetDocShell()->SetDocumentModified();
2150
2151
0
        Point aSplit;
2152
0
        SCCOL nPosX = 1;
2153
0
        SCROW nPosY = 1;
2154
0
        if (eOldV != SC_SPLIT_NONE || eOldH != SC_SPLIT_NONE)
2155
0
        {
2156
0
            if ( eOldV != SC_SPLIT_NONE && (eSplitMethod == SC_SPLIT_METHOD_ROW || eSplitMethod == SC_SPLIT_METHOD_CURSOR))
2157
0
                aSplit.setY( aViewData.GetVSplitPos() - aWinStart.Y() );
2158
2159
0
            if ( eOldH != SC_SPLIT_NONE && (eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_CURSOR))
2160
0
            {
2161
0
                tools::Long nSplitPos = aViewData.GetHSplitPos();
2162
0
                if ( bLayoutRTL )
2163
0
                    nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
2164
0
                aSplit.setX( nSplitPos - aWinStart.X() );
2165
0
            }
2166
2167
0
            aViewData.GetPosFromPixel( aSplit.X(), aSplit.Y(), ePos, nPosX, nPosY );
2168
0
            bool bLeft;
2169
0
            bool bTop;
2170
0
            aViewData.GetMouseQuadrant( aSplit, ePos, nPosX, nPosY, bLeft, bTop );
2171
0
            if (eSplitMethod == SC_SPLIT_METHOD_COL)
2172
0
                nPosX = static_cast<SCCOL>(nFreezeIndex);
2173
0
            else if (!bLeft)
2174
0
                ++nPosX;
2175
0
            if (eSplitMethod == SC_SPLIT_METHOD_ROW)
2176
0
                nPosY = static_cast<SCROW>(nFreezeIndex);
2177
0
            else if (!bTop)
2178
0
                ++nPosY;
2179
0
        }
2180
0
        else
2181
0
        {
2182
0
            switch(eSplitMethod)
2183
0
            {
2184
0
                case SC_SPLIT_METHOD_ROW:
2185
0
                {
2186
0
                    nPosX = 0;
2187
0
                    nPosY = static_cast<SCROW>(nFreezeIndex);
2188
0
                }
2189
0
                break;
2190
0
                case SC_SPLIT_METHOD_COL:
2191
0
                {
2192
0
                    nPosX = static_cast<SCCOL>(nFreezeIndex);
2193
0
                    nPosY = 0;
2194
0
                }
2195
0
                break;
2196
0
                case SC_SPLIT_METHOD_CURSOR:
2197
0
                {
2198
0
                    nPosX = aViewData.GetCurX();
2199
0
                    nPosY = aViewData.GetCurY();
2200
0
                }
2201
0
                break;
2202
0
            }
2203
0
        }
2204
2205
0
        SCROW nTopPos = aViewData.GetPosY(SC_SPLIT_BOTTOM);
2206
0
        SCROW nBottomPos = nPosY;
2207
0
        SCCOL nLeftPos = aViewData.GetPosX(SC_SPLIT_LEFT);
2208
0
        SCCOL nRightPos = nPosX;
2209
2210
0
        if (eSplitMethod == SC_SPLIT_METHOD_ROW || eSplitMethod == SC_SPLIT_METHOD_CURSOR)
2211
0
        {
2212
0
            if (eOldV != SC_SPLIT_NONE)
2213
0
            {
2214
0
                nTopPos = aViewData.GetPosY(SC_SPLIT_TOP);
2215
0
                nBottomPos = std::max(aViewData.GetPosY(SC_SPLIT_BOTTOM), nBottomPos);
2216
0
            }
2217
0
            aSplit = aViewData.GetScrPos(nPosX, nPosY, ePos, true);
2218
0
            if (aSplit.Y() > 0)
2219
0
            {
2220
0
                aViewData.SetVSplitMode(SC_SPLIT_FIX);
2221
0
                aViewData.SetVSplitPos(aSplit.Y() + aWinStart.Y());
2222
0
                aViewData.SetFixPosY(nPosY);
2223
2224
0
                aViewData.SetPosY(SC_SPLIT_TOP, nTopPos);
2225
0
                aViewData.SetPosY(SC_SPLIT_BOTTOM, nBottomPos);
2226
0
            }
2227
0
            else if (nPosY == 1 && eSplitMethod == SC_SPLIT_METHOD_ROW)
2228
0
            {
2229
                // Freeze first row, but row 1 is not visible on screen now == special handling
2230
0
                aViewData.SetVSplitMode(SC_SPLIT_FIX);
2231
0
                aViewData.SetFixPosY(nPosY);
2232
2233
0
                aViewData.SetPosY(SC_SPLIT_TOP, 0);
2234
0
                bUpdateFix = true;
2235
0
            }
2236
0
            else
2237
0
                aViewData.SetVSplitMode(SC_SPLIT_NONE);
2238
0
        }
2239
2240
0
        if (eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_CURSOR)
2241
0
        {
2242
0
            if (eOldH != SC_SPLIT_NONE)
2243
0
            {
2244
0
                if (aViewData.GetPosX(SC_SPLIT_RIGHT) > nRightPos)
2245
0
                    nRightPos = aViewData.GetPosX(SC_SPLIT_RIGHT);
2246
0
            }
2247
0
            aSplit = aViewData.GetScrPos( nPosX, nPosY, ePos, true );
2248
0
            if (nPosX > aViewData.GetPosX(SC_SPLIT_LEFT))       // (aSplit.X() > 0) doesn't work for RTL
2249
0
            {
2250
0
                tools::Long nSplitPos = aSplit.X() + aWinStart.X();
2251
0
                if ( bLayoutRTL )
2252
0
                    nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
2253
2254
0
                aViewData.SetHSplitMode( SC_SPLIT_FIX );
2255
0
                aViewData.SetHSplitPos( nSplitPos );
2256
0
                aViewData.SetFixPosX( nPosX );
2257
2258
0
                aViewData.SetPosX(SC_SPLIT_LEFT, nLeftPos);
2259
0
                aViewData.SetPosX(SC_SPLIT_RIGHT, nRightPos);
2260
0
            }
2261
0
            else if (nPosX == 1 && eSplitMethod == SC_SPLIT_METHOD_COL)
2262
0
            {
2263
                // Freeze first column, but col A is not visible on screen now == special handling
2264
0
                aViewData.SetHSplitMode(SC_SPLIT_FIX);
2265
0
                aViewData.SetFixPosX(nPosX);
2266
2267
0
                aViewData.SetPosX(SC_SPLIT_RIGHT, aViewData.GetPosX(SC_SPLIT_LEFT));
2268
0
                aViewData.SetPosX(SC_SPLIT_LEFT, 0);
2269
0
                bUpdateFix = true;
2270
0
            }
2271
0
            else
2272
0
                aViewData.SetHSplitMode( SC_SPLIT_NONE );
2273
0
        }
2274
0
    }
2275
0
    else                        // unfreeze
2276
0
    {
2277
0
        if ( eOldH == SC_SPLIT_FIX )
2278
0
            aViewData.SetHSplitMode( SC_SPLIT_NORMAL );
2279
0
        if ( eOldV == SC_SPLIT_FIX )
2280
0
            aViewData.SetVSplitMode( SC_SPLIT_NORMAL );
2281
0
    }
2282
2283
    // Form Layer needs to know the visible part of all windows
2284
    // that is why MapMode must already be correct here
2285
0
    SyncGridWindowMapModeFromDrawMapMode();
2286
0
    SetNewVisArea();
2287
2288
0
    RepeatResize(bUpdateFix);
2289
2290
0
    UpdateShow();
2291
0
    PaintLeft();
2292
0
    PaintTop();
2293
0
    PaintGrid();
2294
2295
    //  SC_FOLLOW_NONE: only update active part
2296
0
    AlignToCursor( aViewData.GetCurX(), aViewData.GetCurY(), SC_FOLLOW_NONE );
2297
0
    UpdateAutoFillMark();
2298
2299
0
    InvalidateSplit();
2300
0
}
2301
2302
void ScTabView::RemoveSplit()
2303
0
{
2304
0
    if (aViewData.GetHSplitMode() == SC_SPLIT_FIX || aViewData.GetVSplitMode() == SC_SPLIT_FIX)
2305
0
        aViewData.GetDocShell()->SetDocumentModified();
2306
0
    DoHSplit( 0 );
2307
0
    DoVSplit( 0 );
2308
0
    RepeatResize();
2309
0
}
2310
2311
void ScTabView::SplitAtCursor()
2312
0
{
2313
0
    ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
2314
0
    if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
2315
0
        ePos = SC_SPLIT_TOPLEFT;
2316
0
    vcl::Window* pWin = pGridWin[ePos];
2317
0
    Point aWinStart = pWin->GetPosPixel();
2318
2319
0
    SCCOL nPosX = aViewData.GetCurX();
2320
0
    SCROW nPosY = aViewData.GetCurY();
2321
0
    Point aSplit = aViewData.GetScrPos( nPosX, nPosY, ePos, true );
2322
0
    if ( nPosX > 0 )
2323
0
        DoHSplit( aSplit.X() + aWinStart.X() );
2324
0
    else
2325
0
        DoHSplit( 0 );
2326
0
    if ( nPosY > 0 )
2327
0
        DoVSplit( aSplit.Y() + aWinStart.Y() );
2328
0
    else
2329
0
        DoVSplit( 0 );
2330
0
    RepeatResize();
2331
0
}
2332
2333
void ScTabView::SplitAtPixel( const Point& rPixel )
2334
0
{
2335
    // pixel is relative to the entire View, not to the first GridWin
2336
2337
0
    if ( rPixel.X() > 0 )
2338
0
        DoHSplit( rPixel.X() );
2339
0
    else
2340
0
        DoHSplit( 0 );
2341
0
    if ( rPixel.Y() > 0 )
2342
0
        DoVSplit( rPixel.Y() );
2343
0
    else
2344
0
        DoVSplit( 0 );
2345
0
    RepeatResize();
2346
0
}
2347
2348
void ScTabView::InvalidateSplit()
2349
0
{
2350
0
    SfxBindings& rBindings = aViewData.GetBindings();
2351
0
    rBindings.Invalidate( SID_WINDOW_SPLIT );
2352
0
    rBindings.Invalidate( SID_WINDOW_FIX );
2353
0
    rBindings.Invalidate( SID_WINDOW_FIX_COL );
2354
0
    rBindings.Invalidate( SID_WINDOW_FIX_ROW );
2355
2356
0
    pHSplitter->SetFixed( aViewData.GetHSplitMode() == SC_SPLIT_FIX );
2357
0
    pVSplitter->SetFixed( aViewData.GetVSplitMode() == SC_SPLIT_FIX );
2358
0
}
2359
2360
void ScTabView::SetNewVisArea()
2361
0
{
2362
    //  Draw-MapMode must be set for Controls when VisAreaChanged
2363
    //  (also when Edit-MapMode is set instead)
2364
0
    MapMode aOldMode[4];
2365
0
    MapMode aDrawMode[4];
2366
0
    sal_uInt16 i;
2367
0
    for (i=0; i<4; i++)
2368
0
        if (pGridWin[i])
2369
0
        {
2370
0
            aOldMode[i] = pGridWin[i]->GetMapMode();
2371
0
            aDrawMode[i] = pGridWin[i]->GetDrawMapMode();
2372
0
            if (aDrawMode[i] != aOldMode[i])
2373
0
                pGridWin[i]->SetMapMode(aDrawMode[i]);
2374
0
        }
2375
2376
0
    vcl::Window* pActive = pGridWin[aViewData.GetActivePart()];
2377
0
    if (pActive)
2378
0
        aViewData.GetViewShell()->VisAreaChanged();
2379
0
    if (pDrawView)
2380
0
        pDrawView->VisAreaChanged(nullptr);    // no window passed on -> for all windows
2381
2382
0
    UpdateAllOverlays();                // #i79909# with drawing MapMode set
2383
2384
0
    for (i=0; i<4; i++)
2385
0
        if (pGridWin[i] && aDrawMode[i] != aOldMode[i])
2386
0
        {
2387
0
            pGridWin[i]->SetMapMode(aOldMode[i]);
2388
0
        }
2389
2390
0
    SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
2391
0
    SfxFrame& rFrame = rViewFrame.GetFrame();
2392
0
    css::uno::Reference<css::frame::XController> xController = rFrame.GetController();
2393
0
    if (xController.is())
2394
0
    {
2395
0
        ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
2396
0
        if (pImp)
2397
0
            pImp->VisAreaChanged();
2398
0
    }
2399
2400
0
    if (GetViewData().HasEditView(GetViewData().GetActivePart()))
2401
0
    {
2402
0
        EditView *pEditView = GetViewData().GetEditView(GetViewData().GetActivePart());
2403
0
        vcl::Cursor *pInPlaceCrsr = pEditView->GetCursor();
2404
0
        bool bInPlaceVisCursor = pInPlaceCrsr && pInPlaceCrsr->IsVisible();
2405
2406
0
        if (bInPlaceVisCursor)
2407
0
            pInPlaceCrsr->Hide();
2408
2409
0
        RefeshTextEditOverlay();
2410
2411
0
        if (bInPlaceVisCursor)
2412
0
            pInPlaceCrsr->Show();
2413
0
    }
2414
2415
0
    if (aViewData.GetViewShell()->HasAccessibilityObjects())
2416
0
        aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccVisAreaChanged));
2417
0
}
2418
2419
bool ScTabView::HasPageFieldDataAtCursor() const
2420
0
{
2421
0
    ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
2422
0
    SCCOL nCol = aViewData.GetCurX();
2423
0
    SCROW nRow = aViewData.GetCurY();
2424
0
    if (pWin)
2425
0
        return pWin->GetDPFieldOrientation( nCol, nRow ) == sheet::DataPilotFieldOrientation_PAGE;
2426
2427
0
    return false;
2428
0
}
2429
2430
void ScTabView::StartDataSelect()
2431
0
{
2432
0
    ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
2433
0
    SCCOL nCol = aViewData.GetCurX();
2434
0
    SCROW nRow = aViewData.GetCurY();
2435
2436
0
    if (!pWin)
2437
0
        return;
2438
2439
0
    switch (pWin->GetDPFieldOrientation(nCol, nRow))
2440
0
    {
2441
0
        case sheet::DataPilotFieldOrientation_PAGE:
2442
            //  #i36598# If the cursor is on a page field's data cell,
2443
            //  no meaningful input is possible anyway, so this function
2444
            //  can be used to select a page field entry.
2445
0
            pWin->LaunchPageFieldMenu( nCol, nRow );
2446
0
            return;
2447
0
        case sheet::DataPilotFieldOrientation_COLUMN:
2448
0
        case sheet::DataPilotFieldOrientation_ROW:
2449
0
            pWin->LaunchDPFieldMenu( nCol, nRow );
2450
0
            return;
2451
0
        default:
2452
0
            ;
2453
0
    }
2454
2455
    // Do autofilter if the current cell has autofilter button.  Otherwise do
2456
    // a normal data select popup.
2457
0
    const ScMergeFlagAttr& rAttr =
2458
0
        aViewData.GetDocument().GetAttr(
2459
0
            nCol, nRow, aViewData.CurrentTabForData(), ATTR_MERGE_FLAG);
2460
2461
0
    if (rAttr.HasAutoFilter())
2462
0
        pWin->LaunchAutoFilterMenu(nCol, nRow);
2463
0
    else
2464
0
        pWin->LaunchDataSelectMenu(nCol, nRow);
2465
0
}
2466
2467
void ScTabView::EnableRefInput(bool bFlag)
2468
0
{
2469
0
    aHScrollLeft->EnableInput(bFlag);
2470
0
    aHScrollRight->EnableInput(bFlag);
2471
0
    aVScrollBottom->EnableInput(bFlag);
2472
0
    aVScrollTop->EnableInput(bFlag);
2473
2474
    // from here on dynamically created ones
2475
2476
0
    if(pTabControl!=nullptr) pTabControl->EnableInput(bFlag);
2477
2478
0
    for (auto& p : pGridWin)
2479
0
        if (p)
2480
0
            p->EnableInput(bFlag, false);
2481
0
    for (auto& p : pColBar)
2482
0
        if (p)
2483
0
            p->EnableInput(bFlag, false);
2484
0
    for (auto& p : pRowBar)
2485
0
        if (p)
2486
0
            p->EnableInput(bFlag, false);
2487
0
}
2488
2489
void ScTabView::EnableAutoSpell( bool bEnable )
2490
0
{
2491
0
    const bool bWasEnabled = IsAutoSpell();
2492
0
    if (bEnable)
2493
0
        mpSpellCheckCxt =
2494
0
            std::make_shared<sc::SpellCheckContext>(aViewData.GetDocument(),
2495
0
                                      aViewData.CurrentTabForData());
2496
0
    else
2497
0
        mpSpellCheckCxt.reset();
2498
2499
0
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
2500
0
    {
2501
0
        if (!pWin)
2502
0
            continue;
2503
2504
0
        pWin->SetAutoSpellContext(mpSpellCheckCxt);
2505
0
    }
2506
2507
0
    if (bWasEnabled != bEnable && comphelper::LibreOfficeKit::isActive())
2508
0
    {
2509
0
        if (ScTabViewShell* pViewSh = aViewData.GetViewShell())
2510
0
        {
2511
0
            ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(pViewSh->GetCurrentDocument());
2512
0
            SfxLokHelper::notifyViewRenderState(pViewSh, pModel);
2513
0
        }
2514
0
    }
2515
0
}
2516
2517
bool ScTabView::IsAutoSpell() const
2518
0
{
2519
0
    return static_cast<bool>(mpSpellCheckCxt);
2520
0
}
2521
2522
void ScTabView::ResetAutoSpell()
2523
0
{
2524
0
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
2525
0
    {
2526
0
        if (!pWin)
2527
0
            continue;
2528
2529
0
        pWin->ResetAutoSpell();
2530
0
    }
2531
0
}
2532
2533
void ScTabView::ResetAutoSpellForContentChange()
2534
0
{
2535
0
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
2536
0
    {
2537
0
        if (!pWin)
2538
0
            continue;
2539
2540
0
        pWin->ResetAutoSpellForContentChange();
2541
0
    }
2542
0
}
2543
2544
void ScTabView::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const sc::MisspellRangeResult& rRangeResult )
2545
0
{
2546
0
    for (VclPtr<ScGridWindow> & pWin: pGridWin)
2547
0
    {
2548
0
        if (!pWin)
2549
0
            continue;
2550
2551
0
        pWin->SetAutoSpellData(nPosX, nPosY, rRangeResult);
2552
0
    }
2553
0
}
2554
2555
namespace
2556
{
2557
2558
tools::Long lcl_GetRowHeightPx(const ScViewData &rViewData, SCROW nRow, SCTAB nTab)
2559
0
{
2560
0
    const sal_uInt16 nSize = rViewData.GetDocument().GetRowHeight(nRow, nTab);
2561
0
    return ScViewData::ToPixel(nSize, rViewData.GetPPTY());
2562
0
}
2563
2564
tools::Long lcl_GetColWidthPx(const ScViewData &rViewData, SCCOL nCol, SCTAB nTab)
2565
0
{
2566
0
    const sal_uInt16 nSize = rViewData.GetDocument().GetColWidth(nCol, nTab);
2567
0
    return ScViewData::ToPixel(nSize, rViewData.GetPPTX());
2568
0
}
2569
2570
void lcl_getGroupIndexes(const ScOutlineArray& rArray, SCCOLROW nStart, SCCOLROW nEnd, std::vector<size_t>& rGroupIndexes)
2571
0
{
2572
0
    rGroupIndexes.clear();
2573
0
    const size_t nGroupDepth = rArray.GetDepth();
2574
0
    rGroupIndexes.resize(nGroupDepth);
2575
2576
    // Get first group per each level
2577
0
    for (size_t nLevel = 0; nLevel < nGroupDepth; ++nLevel)
2578
0
    {
2579
0
        if (rArray.GetCount(nLevel))
2580
0
        {
2581
            // look for a group inside the [nStartRow+1, nEndRow] range
2582
0
            size_t nIndex;
2583
0
            bool bFound = rArray.GetEntryIndexInRange(nLevel, nStart + 1, nEnd, nIndex);
2584
0
            if (bFound)
2585
0
            {
2586
0
                if (nIndex > 0)
2587
0
                {
2588
                    // is there a previous group not inside the range
2589
                    // but anyway intersecting it ?
2590
0
                    const ScOutlineEntry* pPrevEntry = rArray.GetEntry(nLevel, nIndex - 1);
2591
0
                    if (pPrevEntry && nStart < pPrevEntry->GetEnd())
2592
0
                    {
2593
0
                        --nIndex;
2594
0
                    }
2595
0
                }
2596
0
            }
2597
0
            else
2598
0
            {
2599
                // look for a group which contains nStartRow+1
2600
0
                bFound = rArray.GetEntryIndex(nLevel, nStart + 1, nIndex);
2601
0
                if (!bFound)
2602
0
                {
2603
                    // look for a group which contains nEndRow
2604
0
                    bFound = rArray.GetEntryIndex(nLevel, nEnd, nIndex);
2605
0
                }
2606
0
            }
2607
2608
0
            if (bFound)
2609
0
            {
2610
                // skip groups with no visible control
2611
0
                bFound = false;
2612
0
                while (nIndex < rArray.GetCount(nLevel))
2613
0
                {
2614
0
                    const ScOutlineEntry* pEntry = rArray.GetEntry(nLevel, nIndex);
2615
0
                    if (pEntry && pEntry->IsVisible())
2616
0
                    {
2617
0
                        bFound = true;
2618
0
                        break;
2619
0
                    }
2620
0
                    if (pEntry && pEntry->GetStart() > nEnd)
2621
0
                    {
2622
0
                        break;
2623
0
                    }
2624
0
                    ++nIndex;
2625
0
                }
2626
0
            }
2627
2628
0
            rGroupIndexes[nLevel] = bFound ? nIndex : -1;
2629
0
        }
2630
0
    }
2631
0
}
2632
2633
void lcl_createGroupsData(
2634
        SCCOLROW nHeaderIndex, SCCOLROW nEnd, tools::Long nSizePx, tools::Long nTotalPx,
2635
        const ScOutlineArray& rArray, std::vector<size_t>& rGroupIndexes,
2636
        std::vector<tools::Long>& rGroupStartPositions, OStringBuffer& rGroupsBuffer)
2637
0
{
2638
0
    const size_t nGroupDepth = rArray.GetDepth();
2639
    // create string data for group controls
2640
0
    for (size_t nLevel = nGroupDepth - 1; nLevel != size_t(-1); --nLevel)
2641
0
    {
2642
0
        size_t nIndex = rGroupIndexes[nLevel];
2643
0
        if (nIndex == size_t(-1))
2644
0
            continue;
2645
0
        const ScOutlineEntry* pEntry = rArray.GetEntry(nLevel, nIndex);
2646
0
        if (pEntry)
2647
0
        {
2648
0
            if (nHeaderIndex < pEntry->GetStart())
2649
0
            {
2650
0
                continue;
2651
0
            }
2652
0
            else if (nHeaderIndex == pEntry->GetStart())
2653
0
            {
2654
0
                rGroupStartPositions[nLevel] = nTotalPx - nSizePx;
2655
0
            }
2656
0
            else if (nHeaderIndex > pEntry->GetStart() && (nHeaderIndex < nEnd && nHeaderIndex < pEntry->GetEnd()))
2657
0
            {
2658
                // for handling group started before the current view range
2659
0
                if (rGroupStartPositions[nLevel] < 0)
2660
0
                    rGroupStartPositions[nLevel] *= -1;
2661
0
                break;
2662
0
            }
2663
0
            if (nHeaderIndex == pEntry->GetEnd() || (nHeaderIndex == nEnd && rGroupStartPositions[nLevel] != -1))
2664
0
            {
2665
                // nHeaderIndex is the end col/row of a group or is the last col/row and a group started and not yet ended
2666
2667
                // append a new group control data
2668
0
                auto len = rGroupsBuffer.getLength();
2669
0
                if (len && rGroupsBuffer[len-1] == '}')
2670
0
                {
2671
0
                    rGroupsBuffer.append(", ");
2672
0
                }
2673
2674
0
                bool bGroupHidden = pEntry->IsHidden();
2675
2676
0
                rGroupsBuffer.append(
2677
0
                    "{ \"level\": " + OString::number(nLevel + 1) + ", "
2678
0
                    "\"index\": " + OString::number(nIndex) + ", "
2679
0
                    "\"startPos\": " + OString::number(rGroupStartPositions[nLevel]) + ", "
2680
0
                    "\"endPos\": " + OString::number(nTotalPx) + ", "
2681
0
                    "\"hidden\": " + OString::number(bGroupHidden ? 1 : 0) + " }");
2682
2683
                // look for the next visible group control at level nLevel
2684
0
                bool bFound = false;
2685
0
                ++nIndex;
2686
0
                while (nIndex < rArray.GetCount(nLevel))
2687
0
                {
2688
0
                    pEntry = rArray.GetEntry(nLevel, nIndex);
2689
0
                    if (pEntry && pEntry->IsVisible())
2690
0
                    {
2691
0
                        bFound = true;
2692
0
                        break;
2693
0
                    }
2694
0
                    if (pEntry && pEntry->GetStart() > nEnd)
2695
0
                    {
2696
0
                        break;
2697
0
                    }
2698
0
                    ++nIndex;
2699
0
                }
2700
0
                rGroupIndexes[nLevel] = bFound ? nIndex : -1;
2701
0
                rGroupStartPositions[nLevel] = -1;
2702
0
            }
2703
0
        }
2704
0
    }
2705
0
}
2706
2707
class ScRangeProvider
2708
{
2709
public:
2710
    ScRangeProvider(const tools::Rectangle& rArea, bool bInPixels,
2711
                    ScViewData& rViewData):
2712
0
        mrViewData(rViewData)
2713
0
    {
2714
0
        tools::Rectangle aAreaPx = bInPixels ? rArea :
2715
0
            tools::Rectangle(rArea.Left() * mrViewData.GetPPTX(),
2716
0
                             rArea.Top() * mrViewData.GetPPTY(),
2717
0
                             rArea.Right() * mrViewData.GetPPTX(),
2718
0
                             rArea.Bottom() * mrViewData.GetPPTY());
2719
0
        calculateBounds(aAreaPx);
2720
0
    }
2721
2722
    const ScRange& getCellRange() const
2723
0
    {
2724
0
        return maRange;
2725
0
    }
2726
2727
    void getColPositions(tools::Long& rStartColPos, tools::Long& rEndColPos) const
2728
0
    {
2729
0
        rStartColPos = maBoundPositions.Left();
2730
0
        rEndColPos = maBoundPositions.Right();
2731
0
    }
2732
2733
    void getRowPositions(tools::Long& rStartRowPos, tools::Long& rEndRowPos) const
2734
0
    {
2735
0
        rStartRowPos = maBoundPositions.Top();
2736
0
        rEndRowPos = maBoundPositions.Bottom();
2737
0
    }
2738
2739
private:
2740
    void calculateBounds(const tools::Rectangle& rAreaPx)
2741
0
    {
2742
0
        tools::Long nLeftPx = 0, nRightPx = 0;
2743
0
        SCCOLROW nStartCol = -1, nEndCol = -1;
2744
0
        calculateDimensionBounds(rAreaPx.Left(), rAreaPx.Right(), true,
2745
0
                                 nStartCol, nEndCol, nLeftPx, nRightPx,
2746
0
                                 mnEnlargeX, mrViewData);
2747
0
        tools::Long nTopPx = 0, nBottomPx = 0;
2748
0
        SCCOLROW nStartRow = -1, nEndRow = -1;
2749
0
        calculateDimensionBounds(rAreaPx.Top(), rAreaPx.Bottom(), false,
2750
0
                                 nStartRow, nEndRow, nTopPx, nBottomPx,
2751
0
                                 mnEnlargeY, mrViewData);
2752
2753
0
        maRange.aStart.Set(nStartCol, nStartRow, mrViewData.CurrentTabForData());
2754
0
        maRange.aEnd.Set(nEndCol, nEndRow, mrViewData.CurrentTabForData());
2755
2756
0
        maBoundPositions.SetLeft(nLeftPx);
2757
0
        maBoundPositions.SetRight(nRightPx);
2758
0
        maBoundPositions.SetTop(nTopPx);
2759
0
        maBoundPositions.SetBottom(nBottomPx);
2760
0
    }
2761
2762
    // All positions are in pixels.
2763
    static void calculateDimensionBounds(const tools::Long nStartPos, const tools::Long nEndPos,
2764
                                         bool bColumns, SCCOLROW& rStartIndex,
2765
                                         SCCOLROW& rEndIndex, tools::Long& rBoundStart,
2766
                                         tools::Long& rBoundEnd, SCCOLROW nEnlarge,
2767
                                         ScViewData& rViewData)
2768
0
    {
2769
0
        ScPositionHelper& rPosHelper = bColumns ? rViewData.GetLOKWidthHelper() :
2770
0
            rViewData.GetLOKHeightHelper();
2771
0
        const auto& rStartNearest = rPosHelper.getNearestByPosition(nStartPos);
2772
0
        const auto& rEndNearest = rPosHelper.getNearestByPosition(nEndPos);
2773
2774
0
        ScBoundsProvider aBoundsProvider(rViewData, rViewData.CurrentTabForData(), bColumns);
2775
0
        aBoundsProvider.Compute(rStartNearest, rEndNearest, nStartPos, nEndPos);
2776
0
        aBoundsProvider.EnlargeBy(nEnlarge);
2777
0
        if (bColumns)
2778
0
        {
2779
0
            SCCOL nStartCol = -1, nEndCol = -1;
2780
0
            aBoundsProvider.GetStartIndexAndPosition(nStartCol, rBoundStart);
2781
0
            aBoundsProvider.GetEndIndexAndPosition(nEndCol, rBoundEnd);
2782
0
            rStartIndex = nStartCol;
2783
0
            rEndIndex = nEndCol;
2784
0
        }
2785
0
        else
2786
0
        {
2787
0
            SCROW nStartRow = -1, nEndRow = -1;
2788
0
            aBoundsProvider.GetStartIndexAndPosition(nStartRow, rBoundStart);
2789
0
            aBoundsProvider.GetEndIndexAndPosition(nEndRow, rBoundEnd);
2790
0
            rStartIndex = nStartRow;
2791
0
            rEndIndex = nEndRow;
2792
0
        }
2793
0
    }
2794
2795
private:
2796
2797
    ScRange maRange;
2798
    tools::Rectangle maBoundPositions;
2799
    ScViewData& mrViewData;
2800
    static const SCCOLROW mnEnlargeX = 2;
2801
    static const SCCOLROW mnEnlargeY = 2;
2802
};
2803
2804
void lcl_ExtendTiledDimension(bool bColumn, const SCCOLROW nEnd, const SCCOLROW nExtra,
2805
                              ScTabView& rTabView, ScViewData& rViewData)
2806
0
{
2807
0
    ScDocument& rDoc = rViewData.GetDocument();
2808
    // If we are approaching current max tiled row/col, signal a size changed event
2809
    // and invalidate the involved area
2810
0
    SCCOLROW nMaxTiledIndex = bColumn ? rViewData.GetMaxTiledCol() : rViewData.GetMaxTiledRow();
2811
0
    SCCOLROW nHardLimit = !bColumn ? MAXTILEDROW : rDoc.MaxCol();
2812
2813
0
    if (nMaxTiledIndex >= nHardLimit)
2814
0
        return;
2815
2816
0
    if (nEnd <= nMaxTiledIndex - nExtra) // No need to extend.
2817
0
        return;
2818
2819
0
    ScDocShell* pDocSh = rViewData.GetDocShell();
2820
0
    ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
2821
0
    Size aOldSize(0, 0);
2822
0
    if (pModelObj)
2823
0
        aOldSize = pModelObj->getDocumentSize();
2824
2825
0
    SCCOLROW nNewMaxTiledIndex = std::min(std::max(nEnd, nMaxTiledIndex) + nExtra, nHardLimit);
2826
2827
0
    if (bColumn)
2828
0
        rViewData.SetMaxTiledCol(nNewMaxTiledIndex);
2829
0
    else
2830
0
        rViewData.SetMaxTiledRow(nNewMaxTiledIndex);
2831
2832
0
    Size aNewSize(0, 0);
2833
0
    if (pModelObj)
2834
0
        aNewSize = pModelObj->getDocumentSize();
2835
2836
0
    if (!pDocSh)
2837
0
        return;
2838
2839
0
    if (pModelObj)
2840
0
    {
2841
0
        ScGridWindow* pGridWindow = rViewData.GetActiveWin();
2842
0
        if (pGridWindow)
2843
0
        {
2844
0
            Size aNewSizePx(aNewSize.Width() * rViewData.GetPPTX(), aNewSize.Height() * rViewData.GetPPTY());
2845
0
            if (aNewSizePx != pGridWindow->GetOutputSizePixel())
2846
0
                pGridWindow->SetOutputSizePixel(aNewSizePx);
2847
0
        }
2848
0
    }
2849
2850
0
    if (aOldSize == aNewSize)
2851
0
        return;
2852
2853
    // New area extended to the right/bottom of the sheet after last col/row
2854
0
    tools::Rectangle aNewArea(Point(0, 0), aNewSize);
2855
    // excluding overlapping area with aNewArea
2856
0
    if (bColumn)
2857
0
        aNewArea.SetLeft(aOldSize.getWidth());
2858
0
    else
2859
0
        aNewArea.SetTop(aOldSize.getHeight());
2860
2861
    // Only invalidate if spreadsheet has extended to the right or bottom
2862
0
    if ((bColumn && aNewArea.getOpenWidth()) || (!bColumn && aNewArea.getOpenHeight()))
2863
0
    {
2864
0
        rTabView.UpdateSelectionOverlay();
2865
0
        SfxLokHelper::notifyInvalidation(rViewData.GetViewShell(), &aNewArea);
2866
0
    }
2867
2868
    // Provide size in the payload, so clients don't have to query for that.
2869
0
    std::stringstream ss;
2870
0
    ss << aNewSize.Width() << ", " << aNewSize.Height();
2871
0
    OString sSize( ss.str() );
2872
0
    ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(
2873
0
        rViewData.GetViewShell()->GetCurrentDocument());
2874
0
    SfxLokHelper::notifyDocumentSizeChanged(rViewData.GetViewShell(), sSize, pModel, false);
2875
0
}
2876
2877
} // anonymous namespace
2878
2879
void ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter)
2880
0
{
2881
0
    ScDocument& rDoc = aViewData.GetDocument();
2882
2883
0
    if (rRectangle.IsEmpty())
2884
0
        return;
2885
2886
0
    bool bRangeHeaderSupport = comphelper::LibreOfficeKit::isRangeHeaders();
2887
2888
0
    rJsonWriter.put("commandName", ".uno:ViewRowColumnHeaders");
2889
2890
0
    SCTAB nTab = aViewData.CurrentTabForData();
2891
0
    SCROW nStartRow = -1;
2892
0
    SCROW nEndRow = -1;
2893
0
    tools::Long nStartHeightPx = 0;
2894
0
    SCCOL nStartCol = -1;
2895
0
    SCCOL nEndCol = -1;
2896
0
    tools::Long nStartWidthPx = 0;
2897
2898
0
    tools::Rectangle aOldVisArea(
2899
0
            mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
2900
0
            mnLOKEndHeaderCol, mnLOKEndHeaderRow);
2901
2902
0
    ScRangeProvider aRangeProvider(rRectangle, /* bInPixels */ false, aViewData);
2903
0
    const ScRange& rCellRange = aRangeProvider.getCellRange();
2904
2905
    /// *** start collecting ROWS ***
2906
2907
    /// 1) compute start and end rows
2908
2909
0
    if (rRectangle.Top() < rRectangle.Bottom())
2910
0
    {
2911
0
        SAL_INFO("sc.lok.header", "Row Header: compute start/end rows.");
2912
0
        tools::Long nEndHeightPx = 0;
2913
0
        nStartRow = rCellRange.aStart.Row();
2914
0
        nEndRow = rCellRange.aEnd.Row();
2915
0
        aRangeProvider.getRowPositions(nStartHeightPx, nEndHeightPx);
2916
2917
0
        aViewData.GetLOKHeightHelper().removeByIndex(mnLOKStartHeaderRow);
2918
0
        aViewData.GetLOKHeightHelper().removeByIndex(mnLOKEndHeaderRow);
2919
0
        aViewData.GetLOKHeightHelper().insert(nStartRow, nStartHeightPx);
2920
0
        aViewData.GetLOKHeightHelper().insert(nEndRow, nEndHeightPx);
2921
2922
0
        mnLOKStartHeaderRow = nStartRow;
2923
0
        mnLOKEndHeaderRow = nEndRow;
2924
0
    }
2925
2926
0
    sal_Int32 nVisibleRows = nEndRow - nStartRow;
2927
0
    if (nVisibleRows < 25)
2928
0
        nVisibleRows = 25;
2929
2930
0
    SAL_INFO("sc.lok.header", "Row Header: visible rows: " << nVisibleRows);
2931
2932
2933
    // Get row groups
2934
    // per each level store the index of the first group intersecting
2935
    // [nStartRow, nEndRow] range
2936
2937
0
    const ScOutlineTable* pTable = rDoc.GetOutlineTable(nTab);
2938
0
    const ScOutlineArray* pRowArray = pTable ? &(pTable->GetRowArray()) : nullptr;
2939
0
    size_t nRowGroupDepth = 0;
2940
0
    std::vector<size_t> aRowGroupIndexes;
2941
0
    if (bRangeHeaderSupport && pTable)
2942
0
    {
2943
0
        nRowGroupDepth = pRowArray->GetDepth();
2944
0
        lcl_getGroupIndexes(*pRowArray, nStartRow, nEndRow, aRowGroupIndexes);
2945
0
    }
2946
2947
    /// 2) if we are approaching current max tiled row, signal a size changed event
2948
    ///    and invalidate the involved area
2949
0
    lcl_ExtendTiledDimension(/* bColumn */ false, nEndRow, nVisibleRows, *this, aViewData);
2950
2951
    /// 3) create string data for rows
2952
2953
0
    tools::Long nTotalPixels = nStartHeightPx;
2954
0
    tools::Long nPrevSizePx = -1;
2955
0
    OStringBuffer aRowGroupsBuffer = "\"rowGroups\": [\n";
2956
0
    {
2957
0
        auto rowsNode = rJsonWriter.startArray("rows");
2958
2959
0
        SAL_INFO("sc.lok.header", "Row Header: [create string data for rows]: start row: "
2960
0
                << nStartRow << " start height: " << nTotalPixels);
2961
2962
0
        if (nStartRow != nEndRow)
2963
0
        {
2964
0
            auto node = rJsonWriter.startStruct();
2965
0
            rJsonWriter.put("text", nStartRow + 1);
2966
0
            rJsonWriter.put("size", nTotalPixels);
2967
0
            rJsonWriter.put("groupLevels", static_cast<sal_Int64>(nRowGroupDepth));
2968
0
        }
2969
2970
0
        std::vector<tools::Long> aRowGroupStartPositions(nRowGroupDepth, -nTotalPixels);
2971
0
        for (SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow)
2972
0
        {
2973
            // nSize will be 0 for hidden rows.
2974
0
            const tools::Long nSizePx = lcl_GetRowHeightPx(aViewData, nRow, nTab);
2975
0
            nTotalPixels += nSizePx;
2976
2977
0
            if (bRangeHeaderSupport && nRowGroupDepth > 0)
2978
0
            {
2979
0
                lcl_createGroupsData(nRow, nEndRow, nSizePx, nTotalPixels,
2980
0
                                     *pRowArray, aRowGroupIndexes, aRowGroupStartPositions,
2981
0
                                     aRowGroupsBuffer);
2982
0
            }
2983
2984
0
            if (bRangeHeaderSupport && nRow < nEndRow && nSizePx == nPrevSizePx)
2985
0
                continue;
2986
0
            nPrevSizePx = nSizePx;
2987
2988
0
            auto node = rJsonWriter.startStruct();
2989
0
            rJsonWriter.put("text", pRowBar[SC_SPLIT_BOTTOM]->GetEntryText(nRow));
2990
0
            rJsonWriter.put("size", nTotalPixels);
2991
0
        }
2992
2993
0
        aRowGroupsBuffer.append("]");
2994
0
    }
2995
0
    if (nRowGroupDepth > 0)
2996
0
    {
2997
0
        aRowGroupsBuffer.append(",\n");
2998
0
        rJsonWriter.putRaw(aRowGroupsBuffer);
2999
0
    }
3000
    ///  end collecting ROWS
3001
3002
3003
    /// *** start collecting COLS ***
3004
3005
    /// 1) compute start and end columns
3006
3007
0
    if (rRectangle.Left() < rRectangle.Right())
3008
0
    {
3009
0
        SAL_INFO("sc.lok.header", "Column Header: compute start/end columns.");
3010
0
        tools::Long nEndWidthPx = 0;
3011
0
        nStartCol = rCellRange.aStart.Col();
3012
0
        nEndCol = rCellRange.aEnd.Col();
3013
0
        aRangeProvider.getColPositions(nStartWidthPx, nEndWidthPx);
3014
3015
0
        aViewData.GetLOKWidthHelper().removeByIndex(mnLOKStartHeaderCol);
3016
0
        aViewData.GetLOKWidthHelper().removeByIndex(mnLOKEndHeaderCol);
3017
0
        aViewData.GetLOKWidthHelper().insert(nStartCol, nStartWidthPx);
3018
0
        aViewData.GetLOKWidthHelper().insert(nEndCol, nEndWidthPx);
3019
3020
0
        mnLOKStartHeaderCol = nStartCol;
3021
0
        mnLOKEndHeaderCol = nEndCol;
3022
0
    }
3023
3024
0
    sal_Int32 nVisibleCols = nEndCol - nStartCol;
3025
0
    if (nVisibleCols < 10)
3026
0
        nVisibleCols = 10;
3027
3028
3029
    // Get column groups
3030
    // per each level store the index of the first group intersecting
3031
    // [nStartCol, nEndCol] range
3032
3033
0
    const ScOutlineArray* pColArray = pTable ? &(pTable->GetColArray()) : nullptr;
3034
0
    size_t nColGroupDepth = 0;
3035
0
    std::vector<size_t> aColGroupIndexes;
3036
0
    if (bRangeHeaderSupport && pTable)
3037
0
    {
3038
0
        nColGroupDepth = pColArray->GetDepth();
3039
0
        lcl_getGroupIndexes(*pColArray, nStartCol, nEndCol, aColGroupIndexes);
3040
0
    }
3041
3042
    /// 2) if we are approaching current max tiled column, signal a size changed event
3043
    ///    and invalidate the involved area
3044
0
    lcl_ExtendTiledDimension(/* bColumn */ true, nEndCol, nVisibleCols, *this, aViewData);
3045
3046
    /// 3) create string data for columns
3047
0
    OStringBuffer aColGroupsBuffer = "\"columnGroups\": [\n";
3048
0
    {
3049
0
        auto columnsNode = rJsonWriter.startArray("columns");
3050
3051
0
        nTotalPixels = nStartWidthPx;
3052
0
        SAL_INFO("sc.lok.header", "Col Header: [create string data for cols]: start col: "
3053
0
                << nStartRow << " start width: " << nTotalPixels);
3054
3055
0
        if (nStartCol != nEndCol)
3056
0
        {
3057
0
            auto node = rJsonWriter.startStruct();
3058
0
            rJsonWriter.put("text", static_cast<sal_Int64>(nStartCol) + 1);
3059
0
            rJsonWriter.put("size", nTotalPixels);
3060
0
            rJsonWriter.put("groupLevels", static_cast<sal_Int64>(nColGroupDepth));
3061
0
        }
3062
3063
0
        std::vector<tools::Long> aColGroupStartPositions(nColGroupDepth, -nTotalPixels);
3064
0
        nPrevSizePx = -1;
3065
0
        for (SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
3066
0
        {
3067
            // nSize will be 0 for hidden columns.
3068
0
            const tools::Long nSizePx = lcl_GetColWidthPx(aViewData, nCol, nTab);
3069
0
            nTotalPixels += nSizePx;
3070
3071
0
            if (bRangeHeaderSupport && nColGroupDepth > 0)
3072
0
                lcl_createGroupsData(nCol, nEndCol, nSizePx, nTotalPixels,
3073
0
                                     *pColArray, aColGroupIndexes,
3074
0
                                     aColGroupStartPositions, aColGroupsBuffer);
3075
3076
0
            if (bRangeHeaderSupport && nCol < nEndCol && nSizePx == nPrevSizePx)
3077
0
                continue;
3078
0
            nPrevSizePx = nSizePx;
3079
3080
0
            OUString aText = bRangeHeaderSupport ?
3081
0
                    OUString::number(nCol + 1) : pColBar[SC_SPLIT_LEFT]->GetEntryText(nCol);
3082
3083
0
            auto node = rJsonWriter.startStruct();
3084
0
            rJsonWriter.put("text", aText);
3085
0
            rJsonWriter.put("size", nTotalPixels);
3086
0
        }
3087
3088
0
        aColGroupsBuffer.append("]");
3089
0
    }
3090
0
    if (nColGroupDepth > 0)
3091
0
    {
3092
0
        aColGroupsBuffer.append(",\n");
3093
0
        rJsonWriter.putRaw(aColGroupsBuffer);
3094
0
    }
3095
    ///  end collecting COLs
3096
3097
0
    vcl::Region aNewVisArea(
3098
0
            tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
3099
0
                    mnLOKEndHeaderCol, mnLOKEndHeaderRow));
3100
0
    aNewVisArea.Exclude(aOldVisArea);
3101
0
    tools::Rectangle aChangedArea = aNewVisArea.GetBoundRect();
3102
0
    if (!aChangedArea.IsEmpty())
3103
0
    {
3104
0
        UpdateVisibleRange();
3105
0
        UpdateFormulas(aChangedArea.Left(), aChangedArea.Top(), aChangedArea.Right(), aChangedArea.Bottom());
3106
0
    }
3107
0
}
3108
3109
OString ScTabView::getSheetGeometryData(bool bColumns, bool bRows, bool bSizes, bool bHidden,
3110
                                        bool bFiltered, bool bGroups)
3111
0
{
3112
0
    ScDocument& rDoc = aViewData.GetDocument();
3113
3114
0
    boost::property_tree::ptree aTree;
3115
0
    aTree.put("commandName", ".uno:SheetGeometryData");
3116
0
    aTree.put("maxtiledcolumn", rDoc.MaxCol());
3117
0
    aTree.put("maxtiledrow", MAXTILEDROW);
3118
3119
0
    auto getJSONString = [](const boost::property_tree::ptree& rTree) {
3120
0
        std::stringstream aStream;
3121
0
        boost::property_tree::write_json(aStream, rTree);
3122
0
        return aStream.str();
3123
0
    };
3124
3125
0
    if ((!bSizes && !bHidden && !bFiltered && !bGroups) ||
3126
0
        (!bColumns && !bRows))
3127
0
    {
3128
0
        return OString(getJSONString(aTree));
3129
0
    }
3130
3131
0
    struct GeomEntry
3132
0
    {
3133
0
        SheetGeomType eType;
3134
0
        const char* pKey;
3135
0
        bool bEnabled;
3136
0
    };
3137
3138
0
    const GeomEntry aGeomEntries[] = {
3139
0
        { SheetGeomType::SIZES,    "sizes",    bSizes    },
3140
0
        { SheetGeomType::HIDDEN,   "hidden",   bHidden   },
3141
0
        { SheetGeomType::FILTERED, "filtered", bFiltered },
3142
0
        { SheetGeomType::GROUPS,   "groups",   bGroups   }
3143
0
    };
3144
3145
0
    struct DimensionEntry
3146
0
    {
3147
0
        const char* pKey;
3148
0
        bool bDimIsCol;
3149
0
        bool bEnabled;
3150
0
    };
3151
3152
0
    const DimensionEntry aDimEntries[] = {
3153
0
        { "columns", true,  bColumns },
3154
0
        { "rows",    false, bRows    }
3155
0
    };
3156
3157
0
    SCTAB nTab = aViewData.CurrentTabForData();
3158
3159
0
    for (const auto& rDimEntry : aDimEntries)
3160
0
    {
3161
0
        if (!rDimEntry.bEnabled)
3162
0
            continue;
3163
3164
0
        bool bDimIsCol = rDimEntry.bDimIsCol;
3165
3166
0
        boost::property_tree::ptree aDimTree;
3167
0
        for (const auto& rGeomEntry : aGeomEntries)
3168
0
        {
3169
0
            if (!rGeomEntry.bEnabled)
3170
0
                continue;
3171
3172
0
            OString aGeomDataEncoding = rDoc.dumpSheetGeomData(nTab, bDimIsCol, rGeomEntry.eType);
3173
            // TODO: Investigate if we can avoid the copy of the 'value' string in put().
3174
0
            aDimTree.put(rGeomEntry.pKey, aGeomDataEncoding.getStr());
3175
0
        }
3176
3177
0
        aTree.add_child(rDimEntry.pKey, aDimTree);
3178
0
    }
3179
3180
0
    return OString(getJSONString(aTree));
3181
0
}
3182
3183
void ScTabView::extendTiledAreaIfNeeded()
3184
0
{
3185
0
    SAL_INFO("sc.lok.header",
3186
0
        "extendTiledAreaIfNeeded: START: ClientView: ColRange["
3187
0
        << mnLOKStartHeaderCol << "," << mnLOKEndHeaderCol
3188
0
        << "] RowRange[" << mnLOKStartHeaderRow << "," << mnLOKEndHeaderRow
3189
0
        << "] MaxTiledCol = " << aViewData.GetMaxTiledCol()
3190
0
        << " MaxTiledRow = " << aViewData.GetMaxTiledRow());
3191
3192
0
    const tools::Rectangle rVisArea = aViewData.getLOKVisibleArea();
3193
0
    if (rVisArea.Top() >= rVisArea.Bottom() ||
3194
0
        rVisArea.Left() >= rVisArea.Right())
3195
0
        return;
3196
3197
    // Needed for conditional updating of visible-range/formula.
3198
0
    tools::Rectangle aOldVisCellRange(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
3199
0
                                      mnLOKEndHeaderCol, mnLOKEndHeaderRow);
3200
3201
0
    ScRangeProvider aRangeProvider(rVisArea, /* bInPixels */ false, aViewData);
3202
    // Index bounds.
3203
0
    const ScRange& rCellRange = aRangeProvider.getCellRange();
3204
0
    const SCCOL nStartCol = rCellRange.aStart.Col();
3205
0
    const SCCOL nEndCol = rCellRange.aEnd.Col();
3206
0
    const SCROW nStartRow = rCellRange.aStart.Row();
3207
0
    const SCROW nEndRow = rCellRange.aEnd.Row();
3208
3209
    // Column/Row positions.
3210
0
    tools::Long nStartColPos, nEndColPos, nStartRowPos, nEndRowPos;
3211
0
    aRangeProvider.getColPositions(nStartColPos, nEndColPos);
3212
0
    aRangeProvider.getRowPositions(nStartRowPos, nEndRowPos);
3213
3214
0
    ScPositionHelper& rWidthHelper = aViewData.GetLOKWidthHelper();
3215
0
    ScPositionHelper& rHeightHelper = aViewData.GetLOKHeightHelper();
3216
3217
    // Update mnLOKStartHeaderCol and mnLOKEndHeaderCol members.
3218
    // These are consulted in some ScGridWindow methods.
3219
0
    if (mnLOKStartHeaderCol != nStartCol)
3220
0
    {
3221
0
        rWidthHelper.removeByIndex(mnLOKStartHeaderCol);
3222
0
        rWidthHelper.insert(nStartCol, nStartColPos);
3223
0
        mnLOKStartHeaderCol = nStartCol;
3224
0
    }
3225
3226
0
    if (mnLOKEndHeaderCol != nEndCol)
3227
0
    {
3228
0
        rWidthHelper.removeByIndex(mnLOKEndHeaderCol);
3229
0
        rWidthHelper.insert(nEndCol, nEndColPos);
3230
0
        mnLOKEndHeaderCol = nEndCol;
3231
0
    }
3232
3233
    // Update mnLOKStartHeaderRow and mnLOKEndHeaderRow members.
3234
    // These are consulted in some ScGridWindow methods.
3235
0
    if (mnLOKStartHeaderRow != nStartRow)
3236
0
    {
3237
0
        rHeightHelper.removeByIndex(mnLOKStartHeaderRow);
3238
0
        rHeightHelper.insert(nStartRow, nStartRowPos);
3239
0
        mnLOKStartHeaderRow = nStartRow;
3240
0
    }
3241
3242
0
    if (mnLOKEndHeaderRow != nEndRow)
3243
0
    {
3244
0
        rHeightHelper.removeByIndex(mnLOKEndHeaderRow);
3245
0
        rHeightHelper.insert(nEndRow, nEndRowPos);
3246
0
        mnLOKEndHeaderRow = nEndRow;
3247
0
    }
3248
3249
0
    constexpr SCCOL nMinExtraCols = 10;
3250
0
    SCCOL nExtraCols = std::max<SCCOL>(nMinExtraCols, nEndCol - nStartCol);
3251
    // If we are approaching current max tiled column, signal a size changed event
3252
    // and invalidate the involved area.
3253
0
    lcl_ExtendTiledDimension(/* bColumn */ true, nEndCol, nExtraCols, *this, aViewData);
3254
3255
0
    constexpr SCROW nMinExtraRows = 25;
3256
0
    SCROW nExtraRows = std::max(nMinExtraRows, nEndRow - nStartRow);
3257
    // If we are approaching current max tiled row, signal a size changed event
3258
    // and invalidate the involved area.
3259
0
    lcl_ExtendTiledDimension(/* bColumn */ false, nEndRow, nExtraRows, *this, aViewData);
3260
3261
0
    vcl::Region aNewVisCellRange(
3262
0
            tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
3263
0
                             mnLOKEndHeaderCol, mnLOKEndHeaderRow));
3264
0
    aNewVisCellRange.Exclude(aOldVisCellRange);
3265
0
    tools::Rectangle aChangedCellRange = aNewVisCellRange.GetBoundRect();
3266
0
    if (!aChangedCellRange.IsEmpty())
3267
0
    {
3268
0
        UpdateVisibleRange();
3269
0
        UpdateFormulas(aChangedCellRange.Left(), aChangedCellRange.Top(),
3270
0
                       aChangedCellRange.Right(), aChangedCellRange.Bottom());
3271
0
    }
3272
3273
0
    SAL_INFO("sc.lok.header",
3274
0
        "extendTiledAreaIfNeeded: END: ClientView: ColRange["
3275
0
        << mnLOKStartHeaderCol << "," << mnLOKEndHeaderCol
3276
0
        << "] RowRange[" << mnLOKStartHeaderRow << "," << mnLOKEndHeaderRow
3277
0
        << "] MaxTiledCol = " << aViewData.GetMaxTiledCol()
3278
0
        << " MaxTiledRow = " << aViewData.GetMaxTiledRow());
3279
0
}
3280
3281
tools::Long ScTabView::GetCurrentPage() const
3282
0
{
3283
0
    std::unique_ptr<ScPageBreakData> pTempPageData;
3284
0
    const ScPageBreakData* pPageData = pPageBreakData.get();
3285
3286
0
    if (!pPageData)
3287
0
    {
3288
0
        ScDocShell* pDocSh = aViewData.GetDocShell();
3289
0
        if (!pDocSh)
3290
0
            return 1;
3291
3292
0
        ScDocument& rDoc = pDocSh->GetDocument();
3293
0
        SCTAB nTab = aViewData.CurrentTabForData();
3294
3295
0
        sal_uInt16 nCount = rDoc.GetPrintRangeCount(nTab);
3296
0
        if (!nCount)
3297
0
            nCount = 1;
3298
3299
0
        pTempPageData = std::make_unique<ScPageBreakData>(nCount);
3300
3301
0
        ScPrintFunc aPrintFunc(
3302
0
            *pDocSh,
3303
0
            pDocSh->GetPrinter(),
3304
0
            nTab,
3305
0
            0, 0,
3306
0
            nullptr,
3307
0
            nullptr,
3308
0
            pTempPageData.get());
3309
3310
        // ScPrintFunc fills the PageBreakData in ctor.
3311
0
        if (nCount > 1)
3312
0
        {
3313
0
            aPrintFunc.ResetBreaks(nTab);
3314
0
            pTempPageData->AddPages();
3315
0
        }
3316
3317
0
        pPageData = pTempPageData.get();
3318
0
    }
3319
3320
0
    const SCCOL nCurCol = aViewData.GetCurX();
3321
0
    const SCROW nCurRow = aViewData.GetCurY();
3322
3323
0
    for (size_t nPos = 0; nPos < pPageData->GetCount(); ++nPos)
3324
0
    {
3325
0
        const ScPrintRangeData& rData = pPageData->GetData(nPos);
3326
0
        const ScRange& rRange = rData.GetPrintRange();
3327
3328
0
        if (nCurCol < rRange.aStart.Col() || nCurCol > rRange.aEnd.Col()
3329
0
            || nCurRow < rRange.aStart.Row() || nCurRow > rRange.aEnd.Row())
3330
0
            continue;
3331
3332
0
        return rData.GetPageNumber(nCurCol, nCurRow);
3333
0
    }
3334
3335
0
    return 1;
3336
0
}
3337
3338
3339
3340
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */