Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/view/gridwin.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
22
#include <cstdlib>
23
#include <memory>
24
#include <editeng/adjustitem.hxx>
25
#include <osl/diagnose.h>
26
#include <sal/log.hxx>
27
#include <sot/storage.hxx>
28
#include <editeng/eeitem.hxx>
29
#include <editeng/editobj.hxx>
30
#include <editeng/editstat.hxx>
31
#include <editeng/editview.hxx>
32
#include <editeng/flditem.hxx>
33
#include <editeng/justifyitem.hxx>
34
#include <editeng/outliner.hxx>
35
#include <editeng/misspellrange.hxx>
36
#include <o3tl/temporary.hxx>
37
#include <o3tl/unit_conversion.hxx>
38
#include <sfx2/dispatch.hxx>
39
#include <sfx2/viewfrm.hxx>
40
#include <sfx2/docfile.hxx>
41
#include <sfx2/ipclient.hxx>
42
#include <svl/stritem.hxx>
43
#include <svl/sharedstringpool.hxx>
44
#include <vcl/canvastools.hxx>
45
#include <vcl/commandevent.hxx>
46
#include <vcl/cursor.hxx>
47
#include <vcl/dialoghelper.hxx>
48
#include <vcl/inputctx.hxx>
49
#include <vcl/ptrstyle.hxx>
50
#include <vcl/settings.hxx>
51
#include <vcl/virdev.hxx>
52
#include <vcl/weld/weldutils.hxx>
53
#include <sot/formats.hxx>
54
#include <comphelper/classids.hxx>
55
#include <comphelper/scopeguard.hxx>
56
57
#include <svx/svdview.hxx>
58
#include <svx/svdocapt.hxx>
59
#include <svx/svdpagv.hxx>
60
#include <svtools/optionsdrawinglayer.hxx>
61
62
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
63
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
64
#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
65
#include <com/sun/star/sheet/MemberResultFlags.hpp>
66
#include <com/sun/star/sheet/TableValidationVisibility.hpp>
67
#include <com/sun/star/awt/KeyModifier.hpp>
68
#include <com/sun/star/awt/MouseButton.hpp>
69
#include <com/sun/star/script/vba/VBAEventId.hpp>
70
#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
71
#include <com/sun/star/text/textfield/Type.hpp>
72
73
#include <com/sun/star/awt/XVclWindowPeer.hdl>
74
75
#include <gridwin.hxx>
76
#include <tabvwsh.hxx>
77
#include <docsh.hxx>
78
#include <viewdata.hxx>
79
#include <tabview.hxx>
80
#include <select.hxx>
81
#include <scmod.hxx>
82
#include <document.hxx>
83
#include <attrib.hxx>
84
#include <dbdata.hxx>
85
#include <stlpool.hxx>
86
#include <printfun.hxx>
87
#include <cbutton.hxx>
88
#include <sc.hrc>
89
#include <helpids.h>
90
#include <globstr.hrc>
91
#include <strings.hrc>
92
#include <editutil.hxx>
93
#include <scresid.hxx>
94
#include <inputhdl.hxx>
95
#include <uiitems.hxx>
96
#include <formulacell.hxx>
97
#include <patattr.hxx>
98
#include <notemark.hxx>
99
#include <rfindlst.hxx>
100
#include <output.hxx>
101
#include <docfunc.hxx>
102
#include <dbdocfun.hxx>
103
#include <dpobject.hxx>
104
#include <transobj.hxx>
105
#include <drwtrans.hxx>
106
#include <seltrans.hxx>
107
#include <sizedev.hxx>
108
#include <AccessibilityHints.hxx>
109
#include <dpsave.hxx>
110
#include <viewuno.hxx>
111
#include <compiler.hxx>
112
#include <editable.hxx>
113
#include <fillinfo.hxx>
114
#include <filterentries.hxx>
115
#include <drwlayer.hxx>
116
#include <validat.hxx>
117
#include <tabprotection.hxx>
118
#include <postit.hxx>
119
#include <dpcontrol.hxx>
120
#include <checklistmenu.hxx>
121
#include <clipparam.hxx>
122
#include <overlayobject.hxx>
123
#include <cellsuno.hxx>
124
#include <drawview.hxx>
125
#include <dragdata.hxx>
126
#include <cliputil.hxx>
127
#include <queryentry.hxx>
128
#include <markdata.hxx>
129
#include <externalrefmgr.hxx>
130
#include <spellcheckcontext.hxx>
131
#include <uiobject.hxx>
132
#include <undoblk.hxx>
133
#include <datamapper.hxx>
134
#include <inputopt.hxx>
135
#include <queryparam.hxx>
136
#include <SparklineList.hxx>
137
138
#include <officecfg/Office/Common.hxx>
139
140
#include <svx/PaletteManager.hxx>
141
#include <svx/sdrpagewindow.hxx>
142
#include <svx/sdr/overlay/overlaymanager.hxx>
143
#include <vcl/svapp.hxx>
144
#include <vcl/uitest/logger.hxx>
145
#include <vcl/uitest/eventdescription.hxx>
146
#include <svx/sdr/overlay/overlayselection.hxx>
147
#include <svx/diagram/DiagramHelper_svx.hxx>
148
#include <comphelper/lok.hxx>
149
#include <sfx2/lokhelper.hxx>
150
151
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
152
#include <tools/json_writer.hxx>
153
154
#include <vector>
155
#include <boost/property_tree/json_parser.hpp>
156
157
#include <FilterListBox.hxx>
158
159
using namespace css;
160
using namespace css::uno;
161
162
struct ScGridWindow::MouseEventState
163
{
164
    bool mbActivatePart;
165
166
    MouseEventState() :
167
0
        mbActivatePart(false)
168
0
    {}
169
};
170
171
0
#define SC_FILTERLISTBOX_LINES  12
172
173
ScGridWindow::VisibleRange::VisibleRange(const ScDocument& rDoc)
174
0
    : mnCol1(0)
175
0
    , mnCol2(rDoc.MaxCol())
176
0
    , mnRow1(0)
177
0
    , mnRow2(rDoc.MaxRow())
178
0
{
179
0
}
180
181
bool ScGridWindow::VisibleRange::isInside(SCCOL nCol, SCROW nRow) const
182
0
{
183
0
    return mnCol1 <= nCol && nCol <= mnCol2 && mnRow1 <= nRow && nRow <= mnRow2;
184
0
}
185
186
bool ScGridWindow::VisibleRange::set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
187
0
{
188
0
    bool bChanged = mnCol1 != nCol1 || mnRow1 != nRow1 || mnCol2 != nCol2 || mnRow2 != nRow2;
189
190
0
    mnCol1 = nCol1;
191
0
    mnRow1 = nRow1;
192
0
    mnCol2 = nCol2;
193
0
    mnRow2 = nRow2;
194
195
0
    return bChanged;
196
0
}
197
198
//  ListBox in a FloatingWindow (pParent)
199
ScFilterListBox::ScFilterListBox(weld::Window* pParent, ScGridWindow* pGrid,
200
                                 SCCOL nNewCol, SCROW nNewRow, ScFilterBoxMode eNewMode)
201
0
    : xBuilder(Application::CreateBuilder(pParent, u"modules/scalc/ui/filterlist.ui"_ustr))
202
0
    , xPopover(xBuilder->weld_popover(u"FilterList"_ustr))
203
0
    , xTreeView(xBuilder->weld_tree_view(u"list"_ustr))
204
0
    , pGridWin(pGrid)
205
0
    , nCol(nNewCol)
206
0
    , nRow(nNewRow)
207
0
    , bInit(true)
208
0
    , bCancelled(false)
209
0
    , bGridHadMouseCaptured(pGrid->IsMouseCaptured())
210
0
    , nSel(0)
211
0
    , eMode(eNewMode)
212
0
    , nAsyncSelectHdl(nullptr)
213
0
{
214
0
    xTreeView->connect_item_activated(LINK(this, ScFilterListBox, SelectHdl));
215
0
    xTreeView->connect_key_press(LINK(this, ScFilterListBox, KeyInputHdl));
216
0
}
217
218
ScFilterListBox::~ScFilterListBox()
219
0
{
220
0
    if (nAsyncSelectHdl)
221
0
    {
222
0
        Application::RemoveUserEvent(nAsyncSelectHdl);
223
0
        nAsyncSelectHdl = nullptr;
224
0
    }
225
0
}
226
227
void ScFilterListBox::EndInit()
228
0
{
229
0
    sal_Int32 nPos = xTreeView->get_selected_index();
230
0
    if (nPos == -1)
231
0
        nSel = 0;
232
0
    else
233
0
        nSel = nPos;
234
235
0
    bInit = false;
236
0
}
237
238
IMPL_LINK(ScFilterListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
239
0
{
240
0
    bool bDone = false;
241
242
0
    vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
243
    // esc with no modifiers
244
0
    if (!aCode.GetModifier() && aCode.GetCode() == KEY_ESCAPE)
245
0
    {
246
0
        pGridWin->ClickExtern();  // clears the listbox
247
0
        bDone = true;
248
0
    }
249
250
    // nowhere to tab to
251
0
    if (aCode.GetCode() == KEY_TAB)
252
0
        bDone = true;
253
254
0
    return bDone;
255
0
}
256
257
IMPL_LINK(ScFilterListBox, SelectHdl, const weld::TreeIter&, rIter, bool)
258
0
{
259
0
    if (!bInit && !bCancelled && !nAsyncSelectHdl)
260
0
    {
261
0
        nSel = xTreeView->get_iter_index_in_parent(rIter);
262
0
        assert(xTreeView->get_iter_index_in_parent(rIter) >= 0
263
0
               && "Invalid index for activated row");
264
        // #i81298# launch async so the box isn't deleted from modifications within FilterSelect
265
0
        nAsyncSelectHdl = Application::PostUserEvent(LINK(this, ScFilterListBox, AsyncSelectHdl));
266
0
    }
267
0
    return true;
268
0
}
269
270
IMPL_LINK_NOARG(ScFilterListBox, AsyncSelectHdl, void*, void)
271
0
{
272
0
    nAsyncSelectHdl = nullptr;
273
274
    //tdf#133971 hold self-ref until we return
275
0
    auto xThis(shared_from_this());
276
0
    pGridWin->FilterSelect(nSel);
277
0
    if (xThis.use_count() == 1)
278
0
    {
279
        // tdf#133855 we got disposed by FilterSelect
280
0
        return;
281
0
    }
282
0
    pGridWin->ClickExtern();
283
0
}
284
285
static bool lcl_IsEditableMatrix( ScDocument& rDoc, const ScRange& rRange )
286
0
{
287
    // If it is an editable range and if there is a Matrix cell at the bottom right with an
288
    // origin top left then the range will be set to contain the exact matrix.
289
    //! Extract the MatrixEdges functions directly from the column ???
290
0
    if ( !rDoc.IsBlockEditable( rRange.aStart.Tab(), rRange.aStart.Col(),rRange.aStart.Row(),
291
0
                                    rRange.aEnd.Col(),rRange.aEnd.Row() ) )
292
0
        return false;
293
294
0
    ScRefCellValue aCell(rDoc, rRange.aEnd);
295
0
    ScAddress aPos;
296
0
    return (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetMatrixOrigin(rDoc, aPos) && aPos == rRange.aStart);
297
0
}
298
299
static void lcl_UnLockComment( ScDrawView* pView, const Point& rPos, const ScViewData& rViewData )
300
0
{
301
0
    if (!pView)
302
0
        return;
303
304
0
    ScDocument& rDoc = rViewData.GetDocument();
305
0
    ScAddress aCellPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.CurrentTabForData() );
306
0
    ScPostIt* pNote = rDoc.GetNote( aCellPos );
307
0
    SdrObject* pObj = pNote ? pNote->GetCaption() : nullptr;
308
0
    if( pObj && pObj->GetLogicRect().Contains( rPos ) && ScDrawLayer::IsNoteCaption( pObj ) )
309
0
    {
310
0
        const ScProtectionAttr& rProtAttr = rDoc.GetAttr( aCellPos, ATTR_PROTECTION );
311
0
        bool bProtectAttr = rProtAttr.GetProtection() || rProtAttr.GetHideCell() ;
312
0
        bool bProtectDoc =  rDoc.IsTabProtected( aCellPos.Tab() ) || rViewData.GetSfxDocShell()->IsReadOnly() ;
313
        // unlock internal layer (if not protected), will be relocked in ScDrawView::MarkListHasChanged()
314
0
        pView->LockInternalLayer( bProtectDoc && bProtectAttr );
315
0
    }
316
0
}
317
318
static bool lcl_GetHyperlinkCell(
319
    ScDocument& rDoc, SCCOL& rPosX, SCROW nPosY, SCTAB nTab, ScRefCellValue& rCell, OUString& rURL )
320
0
{
321
0
    bool bFound = false;
322
0
    do
323
0
    {
324
0
        ScAddress aPos(rPosX, nPosY, nTab);
325
0
        rCell.assign(rDoc, aPos);
326
0
        if (rCell.isEmpty())
327
0
        {
328
0
            if ( rPosX <= 0 )
329
0
                return false;                           // everything empty to the links
330
0
            else
331
0
                --rPosX;                                // continue search
332
0
        }
333
0
        else
334
0
        {
335
0
            const ScPatternAttr* pPattern = rDoc.GetPattern(aPos);
336
0
            if ( !pPattern->GetItem(ATTR_HYPERLINK).GetValue().isEmpty() )
337
0
            {
338
0
                rURL = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
339
0
                bFound = true;
340
0
            }
341
0
            else if (rCell.getType() == CELLTYPE_EDIT)
342
0
                bFound = true;
343
0
            else if (rCell.getType() == CELLTYPE_FORMULA && rCell.getFormula()->IsHyperLinkCell())
344
0
                bFound = true;
345
0
            else
346
0
                return false;                               // other cell
347
0
        }
348
0
    }
349
0
    while ( !bFound );
350
351
0
    return bFound;
352
0
}
353
354
static void lcl_GetMirror(Point& rPoint, tools::Rectangle& rRect, const tools::Long nWidth)
355
0
{
356
0
    tools::Long nLeft = rRect.Left();
357
0
    tools::Long nRight = rRect.Right();
358
0
    tools::Long nMirrorPX = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px);
359
0
    tools::Long nMirrorMM = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);
360
361
0
    rPoint.setX(nMirrorPX - rPoint.X());
362
0
    rRect.SetLeft(nMirrorMM - nRight);
363
0
    rRect.SetRight(nMirrorMM - nLeft);
364
0
}
365
366
//  WB_DIALOGCONTROL needed for UNO-Controls
367
ScGridWindow::ScGridWindow( vcl::Window* pParent, ScViewData& rData, ScSplitPos eWhichPos )
368
0
:           DocWindow( pParent, WB_CLIPCHILDREN | WB_DIALOGCONTROL ),
369
0
            DropTargetHelper( this ),
370
0
            DragSourceHelper( this ),
371
0
            maVisibleRange(rData.GetDocument()),
372
0
            mrViewData( rData ),
373
0
            eWhich( eWhichPos ),
374
0
            nCursorHideCount( 0 ),
375
0
            nButtonDown( 0 ),
376
0
            nMouseStatus( SC_GM_NONE ),
377
0
            nNestedButtonState( ScNestedButtonState::NONE ),
378
0
            nDPField( 0 ),
379
0
            pDragDPObj( nullptr ),
380
0
            nRFIndex( 0 ),
381
0
            nRFAddX( 0 ),
382
0
            nRFAddY( 0 ),
383
0
            nPagebreakMouse( SC_PD_NONE ),
384
0
            nPagebreakBreak( 0 ),
385
0
            nPagebreakPrev( 0 ),
386
0
            nPageScript( SvtScriptType::NONE ),
387
0
            nDragStartX( -1 ),
388
0
            nDragStartY( -1 ),
389
0
            nDragEndX( -1 ),
390
0
            nDragEndY( -1 ),
391
0
            meDragInsertMode( INS_NONE ),
392
0
            aComboButton( GetOutDev() ),
393
0
            aCurMousePos( 0,0 ),
394
0
            nPaintCount( 0 ),
395
0
            aDrawMouseDownPos( 0,0 ),
396
0
            aRFSelectedCorned( NONE ),
397
0
            maShowPageBreaksTimer("ScGridWindow maShowPageBreaksTimer"),
398
0
            bEEMouse( false ),
399
0
            bDPMouse( false ),
400
0
            bRFMouse( false ),
401
0
            bRFSize( false ),
402
0
            bPagebreakDrawn( false ),
403
0
            bDragRect( false ),
404
0
            bIsInPaint( false ),
405
0
            bNeedsRepaint( false ),
406
0
            bAutoMarkVisible( false ),
407
0
            bListValButton( false ),
408
0
            m_nDownPosX( -1 ),
409
0
            m_nDownPosY( -1 )
410
0
{
411
0
    set_id(u"grid_window"_ustr);
412
0
    switch(eWhich)
413
0
    {
414
0
        case SC_SPLIT_TOPLEFT:
415
0
            eHWhich = SC_SPLIT_LEFT;
416
0
            eVWhich = SC_SPLIT_TOP;
417
0
            break;
418
0
        case SC_SPLIT_TOPRIGHT:
419
0
            eHWhich = SC_SPLIT_RIGHT;
420
0
            eVWhich = SC_SPLIT_TOP;
421
0
            break;
422
0
        case SC_SPLIT_BOTTOMLEFT:
423
0
            eHWhich = SC_SPLIT_LEFT;
424
0
            eVWhich = SC_SPLIT_BOTTOM;
425
0
            break;
426
0
        case SC_SPLIT_BOTTOMRIGHT:
427
0
            eHWhich = SC_SPLIT_RIGHT;
428
0
            eVWhich = SC_SPLIT_BOTTOM;
429
0
            break;
430
0
        default:
431
0
            OSL_FAIL("GridWindow: wrong position");
432
0
    }
433
434
0
    SetUseFrameData(comphelper::LibreOfficeKit::isActive());
435
0
    SetBackground();
436
437
0
    SetMapMode(mrViewData.GetLogicMode(eWhich));
438
0
    EnableChildTransparentMode();
439
0
    SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );
440
441
0
    SetHelpId( HID_SC_WIN_GRIDWIN );
442
443
0
    GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
444
0
    EnableRTL( false );
445
446
0
    bInitialPageBreaks = true;
447
0
    maShowPageBreaksTimer.SetInvokeHandler(LINK(this, ScGridWindow, InitiatePageBreaksTimer));
448
0
    maShowPageBreaksTimer.SetTimeout(1);
449
0
}
Unexecuted instantiation: ScGridWindow::ScGridWindow(vcl::Window*, ScViewData&, ScSplitPos)
Unexecuted instantiation: ScGridWindow::ScGridWindow(vcl::Window*, ScViewData&, ScSplitPos)
450
451
ScGridWindow::~ScGridWindow()
452
0
{
453
0
    disposeOnce();
454
0
}
455
456
void ScGridWindow::dispose()
457
0
{
458
0
    maShowPageBreaksTimer.Stop();
459
460
0
    ImpDestroyOverlayObjects();
461
462
0
    mpFilterBox.reset();
463
0
    mpNoteOverlay.reset();
464
0
    mpAutoFilterPopup.reset();
465
0
    mpDPFieldPopup.reset();
466
0
    aComboButton.SetOutputDevice(nullptr);
467
468
0
    if (mpSpellCheckCxt)
469
0
        mpSpellCheckCxt->reset();
470
0
    mpSpellCheckCxt.reset();
471
472
0
    vcl::Window::dispose();
473
0
}
474
475
void ScGridWindow::ClickExtern()
476
0
{
477
0
    do
478
0
    {
479
        // #i84277# when initializing the filter box, a Basic error can deactivate the view
480
0
        if (mpFilterBox && mpFilterBox->IsInInit())
481
0
            break;
482
0
        mpFilterBox.reset();
483
0
    }
484
0
    while (false);
485
486
0
    if (mpDPFieldPopup)
487
0
    {
488
0
        mpDPFieldPopup->close(false);
489
0
        mpDPFieldPopup.reset();
490
0
    }
491
0
}
492
493
IMPL_LINK_NOARG(ScGridWindow, PopupModeEndHdl, weld::Popover&, void)
494
0
{
495
0
    if (mpFilterBox)
496
0
    {
497
0
        bool bMouseWasCaptured = mpFilterBox->MouseWasCaptured();
498
0
        mpFilterBox->SetCancelled();     // cancel select
499
        // restore the mouse capture state of the GridWindow to
500
        // what it was at initial popup time
501
0
        SAL_WARN_IF(bMouseWasCaptured, "sc.ui", "Is there a scenario where the mouse was captured before mouse down?");
502
0
        if (bMouseWasCaptured)
503
0
            CaptureMouse();
504
0
    }
505
0
    GrabFocus();
506
0
}
507
508
IMPL_LINK( ScGridWindow, PopupSpellingHdl, SpellCallbackInfo&, rInfo, void )
509
0
{
510
0
    if( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
511
0
        mrViewData.GetDispatcher().Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON );
512
0
    else if (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS)
513
0
        mrViewData.GetDispatcher().Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON );
514
0
    else //IGNOREWORD, ADDTODICTIONARY, WORDLANGUAGE, PARALANGUAGE
515
0
    {
516
        // The spelling status of the word has changed. Close the cell to reset the caches
517
0
        ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
518
0
        if (pHdl)
519
0
            pHdl->EnterHandler();
520
0
    }
521
0
}
522
523
namespace {
524
525
struct AutoFilterData : public ScCheckListMenuControl::ExtendedData
526
{
527
    ScAddress maPos;
528
    ScDBData* mpData;
529
};
530
531
class AutoFilterAction : public ScCheckListMenuControl::Action
532
{
533
protected:
534
    VclPtr<ScGridWindow> mpWindow;
535
    ScGridWindow::AutoFilterMode meMode;
536
public:
537
    AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
538
0
        mpWindow(p), meMode(eMode) {}
539
    virtual bool execute() override
540
0
    {
541
0
        mpWindow->UpdateAutoFilterFromMenu(meMode);
542
        // UpdateAutoFilterFromMenu manually closes the popup so return
543
        // false to not attempt a second close
544
0
        return false;
545
0
    }
546
};
547
548
class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
549
{
550
    VclPtr<ScGridWindow> mpWindow;
551
    ScAddress maPos;
552
public:
553
    AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
554
0
        mpWindow(p), maPos(rPos) {}
555
    virtual bool execute() override
556
0
    {
557
0
        mpWindow->RefreshAutoFilterButton(maPos);
558
0
        mpWindow->GrabFocus();
559
0
        return false; // this is called after the popup has been closed
560
0
    }
561
};
562
563
class AutoFilterSubMenuAction : public AutoFilterAction
564
{
565
protected:
566
    ScListSubMenuControl* m_pSubMenu;
567
568
public:
569
    AutoFilterSubMenuAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode)
570
0
        : AutoFilterAction(p, eMode)
571
0
        , m_pSubMenu(pSubMenu)
572
0
    {
573
0
    }
574
};
575
576
class AutoFilterColorAction : public AutoFilterSubMenuAction
577
{
578
private:
579
    Color m_aColor;
580
581
public:
582
    AutoFilterColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor)
583
0
        : AutoFilterSubMenuAction(p, pSubMenu, eMode)
584
0
        , m_aColor(rColor)
585
0
    {
586
0
    }
587
588
    virtual bool execute() override
589
0
    {
590
0
        const AutoFilterData* pData =
591
0
            static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
592
593
0
        if (!pData)
594
0
            return false;
595
596
0
        ScDBData* pDBData = pData->mpData;
597
0
        if (!pDBData)
598
0
            return false;
599
600
0
        const ScAddress& rPos = pData->maPos;
601
602
0
        ScViewData& rViewData = m_pSubMenu->GetViewData();
603
0
        ScDocument& rDoc = rViewData.GetDocument();
604
605
0
        ScQueryParam aParam;
606
0
        pDBData->GetQueryParam(aParam);
607
608
        // Try to use the existing entry for the column (if one exists).
609
0
        ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
610
611
0
        if (!pEntry)
612
0
        {
613
            // Something went terribly wrong!
614
0
            return false;
615
0
        }
616
617
0
        if (ScTabViewShell::isAnyEditViewInRange(rViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
618
0
            return false;
619
620
0
        pEntry->bDoQuery = true;
621
0
        pEntry->nField = rPos.Col();
622
0
        pEntry->eConnect = SC_AND;
623
624
0
        ScFilterEntries aFilterEntries;
625
0
        rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
626
627
0
        bool bActive = false;
628
0
        ScQueryEntry::Item& rItem = pEntry->GetQueryItem();
629
0
        if (rItem.maColor == m_aColor
630
0
            && ((meMode == ScGridWindow::AutoFilterMode::TextColor
631
0
                 && rItem.meType == ScQueryEntry::ByTextColor)
632
0
                || (meMode == ScGridWindow::AutoFilterMode::BackgroundColor
633
0
                    && rItem.meType == ScQueryEntry::ByBackgroundColor)))
634
0
        {
635
0
            bActive = true;
636
0
        }
637
638
        // Disable color filter when active color was selected
639
0
        if (bActive)
640
0
        {
641
0
            aParam.RemoveAllEntriesByField(rPos.Col());
642
0
            pEntry = nullptr;   // invalidated by RemoveAllEntriesByField call
643
644
            // tdf#46184 reset filter options to default values
645
0
            aParam.eSearchType = utl::SearchParam::SearchType::Normal;
646
0
            aParam.bCaseSens = false;
647
0
            aParam.bDuplicate = true;
648
0
            aParam.bInplace = true;
649
0
        }
650
0
        else
651
0
        {
652
0
            if (meMode == ScGridWindow::AutoFilterMode::TextColor)
653
0
                pEntry->SetQueryByTextColor(m_aColor);
654
0
            else
655
0
                pEntry->SetQueryByBackgroundColor(m_aColor);
656
0
        }
657
658
0
        rViewData.GetView()->Query(aParam, nullptr, true);
659
0
        pDBData->SetQueryParam(aParam);
660
661
0
        return true;
662
0
    }
663
};
664
665
class AutoFilterSortColorAction : public AutoFilterSubMenuAction
666
{
667
private:
668
    Color m_aColor;
669
    ScViewData& m_rViewData;
670
671
public:
672
    AutoFilterSortColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor, ScViewData& rViewData)
673
0
        : AutoFilterSubMenuAction(p, pSubMenu, eMode)
674
0
        , m_aColor(rColor)
675
0
        , m_rViewData(rViewData)
676
0
    {
677
0
    }
678
679
    virtual bool execute() override
680
0
    {
681
0
        const AutoFilterData* pData =
682
0
            static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
683
684
0
        if (!pData)
685
0
            return false;
686
687
0
        ScDBData* pDBData = pData->mpData;
688
0
        if (!pDBData)
689
0
            return false;
690
691
0
        const ScAddress& rPos = pData->maPos;
692
0
        SCCOL nCol = rPos.Col();
693
0
        ScSortParam aSortParam;
694
0
        pDBData->GetSortParam(aSortParam);
695
0
        if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
696
            // out of bound
697
0
            return false;
698
699
0
        bool bHasHeader = pDBData->HasHeader();
700
701
0
        aSortParam.bHasHeader = bHasHeader;
702
0
        aSortParam.bByRow = true;
703
0
        aSortParam.bCaseSens = false;
704
0
        aSortParam.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
705
0
        aSortParam.aDataAreaExtras.mbCellNotes = false;
706
0
        aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
707
0
        aSortParam.aDataAreaExtras.mbCellFormats = true;
708
0
        aSortParam.bInplace = true;
709
0
        aSortParam.maKeyState[0].bDoSort = true;
710
0
        aSortParam.maKeyState[0].nField = nCol;
711
0
        aSortParam.maKeyState[0].bAscending = true;
712
0
        aSortParam.maKeyState[0].aColorSortMode = meMode == ScGridWindow::AutoFilterMode::TextColor
713
0
                                                      ? ScColorSortMode::TextColor
714
0
                                                      : ScColorSortMode::BackgroundColor;
715
0
        aSortParam.maKeyState[0].aColorSortColor = m_aColor;
716
717
0
        for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
718
0
            aSortParam.maKeyState[i].bDoSort = false;
719
720
0
        m_rViewData.GetViewShell()->UISort(aSortParam);
721
722
0
        return true;
723
0
    }
724
};
725
726
class AutoFilterColorPopupStartAction : public AutoFilterSubMenuAction
727
{
728
private:
729
    bool mbIsFilter;
730
public:
731
    AutoFilterColorPopupStartAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, bool bIsFilter)
732
0
        : AutoFilterSubMenuAction(p, pSubMenu, ScGridWindow::AutoFilterMode::Normal),
733
0
        mbIsFilter(bIsFilter)
734
0
    {
735
0
    }
736
737
    virtual bool execute() override
738
0
    {
739
0
        const AutoFilterData* pData =
740
0
            static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
741
742
0
        if (!pData)
743
0
            return false;
744
745
0
        ScDBData* pDBData = pData->mpData;
746
0
        if (!pDBData)
747
0
            return false;
748
749
0
        ScViewData& rViewData = m_pSubMenu->GetViewData();
750
0
        ScDocument& rDoc = rViewData.GetDocument();
751
0
        const ScAddress& rPos = pData->maPos;
752
753
0
        ScFilterEntries aFilterEntries;
754
0
        rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
755
756
0
        m_pSubMenu->clearMenuItems();
757
758
0
        XColorListRef xUserColorList;
759
760
0
        OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
761
0
        PaletteManager aPaletteManager;
762
0
        std::vector<OUString> aPaletteNames = aPaletteManager.GetPaletteList();
763
0
        for (size_t i = 0, nLen = aPaletteNames.size(); i < nLen; ++i)
764
0
        {
765
0
            if (aPaletteName == aPaletteNames[i])
766
0
            {
767
0
                aPaletteManager.SetPalette(i);
768
0
                xUserColorList = XPropertyList::AsColorList(
769
0
                                XPropertyList::CreatePropertyListFromURL(
770
0
                                XPropertyListType::Color, aPaletteManager.GetSelectedPalettePath()));
771
0
                if (!xUserColorList->Load())
772
0
                    xUserColorList = nullptr;
773
0
                break;
774
0
            }
775
0
        }
776
777
0
        ScQueryParam aParam;
778
0
        pDBData->GetQueryParam(aParam);
779
0
        ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
780
781
0
        int nMenu = 0;
782
0
        for (auto eMode : {ScGridWindow::AutoFilterMode::BackgroundColor, ScGridWindow::AutoFilterMode::TextColor})
783
0
        {
784
0
            std::set<Color> aColors = eMode == ScGridWindow::AutoFilterMode::TextColor
785
0
                                          ? aFilterEntries.getTextColors()
786
0
                                          : aFilterEntries.getBackgroundColors();
787
788
0
            for (auto& rColor : aColors)
789
0
            {
790
0
                bool bActive = false;
791
792
0
                if (pEntry)
793
0
                {
794
0
                    ScQueryEntry::Item& rItem = pEntry->GetQueryItem();
795
0
                    if (rItem.maColor == rColor
796
0
                        && ((eMode == ScGridWindow::AutoFilterMode::TextColor
797
0
                             && rItem.meType == ScQueryEntry::ByTextColor)
798
0
                            || (eMode == ScGridWindow::AutoFilterMode::BackgroundColor
799
0
                                && rItem.meType == ScQueryEntry::ByBackgroundColor)))
800
0
                    {
801
0
                        bActive = true;
802
0
                    }
803
0
                }
804
805
0
                const bool bAutoColor = rColor == COL_AUTO;
806
807
                // ColorListBox::ShowPreview is similar
808
0
                ScopedVclPtr<VirtualDevice> xDev(m_pSubMenu->create_virtual_device());
809
0
                const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
810
0
                Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
811
0
                xDev->SetOutputSize(aImageSize);
812
0
                const tools::Rectangle aRect(Point(0, 0), aImageSize);
813
814
0
                if (bAutoColor)
815
0
                {
816
0
                    const Color aW(COL_WHITE);
817
0
                    const Color aG(0xef, 0xef, 0xef);
818
0
                    int nMinDim = std::min(aImageSize.Width(), aImageSize.Height()) + 1;
819
0
                    int nCheckSize = nMinDim / 3;
820
0
                    xDev->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), std::min(nCheckSize, 8), aW, aG);
821
0
                    xDev->SetFillColor();
822
0
                }
823
0
                else
824
0
                    xDev->SetFillColor(rColor);
825
826
0
                xDev->SetLineColor(rStyleSettings.GetDisableColor());
827
0
                xDev->DrawRect(aRect);
828
829
0
                if (bAutoColor)
830
0
                {
831
0
                    OUString sText = eMode == ScGridWindow::AutoFilterMode::TextColor
832
0
                                         ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
833
0
                                         : ScResId(SCSTR_FILTER_NO_FILL);
834
0
                    if (mbIsFilter)
835
0
                    {
836
0
                        m_pSubMenu->addMenuColorItem(
837
0
                            sText, bActive, *xDev, nMenu,
838
0
                            new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
839
0
                    }
840
0
                    else
841
0
                    {
842
0
                        m_pSubMenu->addMenuColorItem(
843
0
                            sText, bActive, *xDev, nMenu,
844
0
                            new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor, rViewData));
845
0
                    }
846
0
                }
847
0
                else
848
0
                {
849
0
                    OUString sName;
850
851
0
                    bool bFoundColorName = false;
852
0
                    if (xUserColorList)
853
0
                    {
854
0
                        sal_Int32 nPos = xUserColorList->GetIndexOfColor(rColor);
855
0
                        if (nPos != -1)
856
0
                        {
857
0
                            XColorEntry* pColorEntry = xUserColorList->GetColor(nPos);
858
0
                            sName = pColorEntry->GetName();
859
0
                            bFoundColorName = true;
860
0
                        }
861
0
                    }
862
0
                    if (!bFoundColorName)
863
0
                        sName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
864
865
0
                    if (mbIsFilter)
866
0
                    {
867
0
                        m_pSubMenu->addMenuColorItem(
868
0
                            sName, bActive, *xDev, nMenu,
869
0
                            new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
870
0
                    }
871
0
                    else
872
0
                    {
873
0
                        m_pSubMenu->addMenuColorItem(
874
0
                            sName, bActive, *xDev, nMenu,
875
0
                            new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor,
876
0
                                                          rViewData));
877
0
                    }
878
0
                }
879
0
            }
880
881
0
            ++nMenu;
882
0
        }
883
884
0
        m_pSubMenu->resizeToFitMenuItems();
885
886
0
        return false;
887
0
    }
888
};
889
890
class AddItemToEntry
891
{
892
    ScQueryEntry::QueryItemsType& mrItems;
893
    svl::SharedStringPool& mrPool;
894
public:
895
    AddItemToEntry(ScQueryEntry::QueryItemsType& rItems, svl::SharedStringPool& rPool) :
896
0
        mrItems(rItems), mrPool(rPool) {}
897
    void operator() (const ScCheckListMenuControl::ResultEntry& rEntry)
898
0
    {
899
0
        if (rEntry.bValid)
900
0
        {
901
0
            ScQueryEntry::Item aNew;
902
0
            aNew.maString = mrPool.intern(rEntry.aName);
903
            // set the filter type to ByValue, if the filter condition is value
904
0
            aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : rEntry.bValue ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
905
0
            aNew.mfVal = rEntry.nValue;
906
0
            mrItems.push_back(aNew);
907
0
        }
908
0
    }
909
};
910
911
class AddSelectedItemString
912
{
913
    std::unordered_set<OUString>& mrSetString;
914
    std::unordered_set<double>& mrSetValue;
915
public:
916
    explicit AddSelectedItemString(std::unordered_set<OUString>& rString, std::unordered_set<double>& rValue) :
917
0
        mrSetString(rString), mrSetValue(rValue) {}
918
919
    void operator() (const ScQueryEntry::Item& rItem)
920
0
    {
921
0
        if( rItem.meType == ScQueryEntry::QueryType::ByValue )
922
0
            mrSetValue.insert(rItem.mfVal);
923
0
        else
924
0
            mrSetString.insert(rItem.maString.getString());
925
0
    }
926
};
927
928
void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUString& aevent)
929
0
{
930
0
    EventDescription aDescription;
931
0
    aDescription.aAction = "LAUNCH";
932
0
    aDescription.aID = "grid_window";
933
0
    aDescription.aParameters = {{aevent, ""},
934
0
        {"ROW", aRow}, {"COL", aCol}};
935
0
    aDescription.aParent = "MainWindow";
936
0
    aDescription.aKeyWord = "ScGridWinUIObject";
937
938
0
    UITestLogger::getInstance().logEvent(aDescription);
939
0
}
940
941
}
942
943
0
void ScGridWindow::SendAutofilterPopupPosition(SCCOL nCol, SCROW nRow) {
944
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
945
0
    if (pViewShell)
946
0
    {
947
0
        tools::JsonWriter writer;
948
0
        writer.put("commandName", "AutoFilterInfo");
949
0
        {
950
0
            const auto aState = writer.startNode("state");
951
0
            writer.put("column", nCol);
952
0
            writer.put("row", nRow);
953
0
        }
954
0
        OString info = writer.finishAndGetAsOString();
955
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, info);
956
0
    }
957
0
}
958
959
0
void ScGridWindow::SendAutofilterChange() {
960
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
961
0
    if (pViewShell)
962
0
    {
963
0
        tools::JsonWriter writer;
964
0
        writer.put("commandName", "AutoFilterChange");
965
0
        writer.put("state", true);
966
0
        OString info = writer.finishAndGetAsOString();
967
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, info);
968
0
    }
969
0
}
970
971
void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow)
972
0
{
973
0
    SCTAB nTab = mrViewData.CurrentTabForData();
974
0
    ScDocument& rDoc = mrViewData.GetDocument();
975
0
    bool bLOKActive = comphelper::LibreOfficeKit::isActive();
976
977
0
    mpAutoFilterPopup.reset();
978
979
    // Estimate the width (in pixels) of the longest text in the list
980
0
    ScFilterEntries aFilterEntries;
981
0
    rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);
982
983
0
    weld::Window* pPopupParent = GetFrameWeld();
984
0
    int nColWidth = ScViewData::ToPixel(rDoc.GetColWidth(nCol, nTab), mrViewData.GetPPTX());
985
0
    mpAutoFilterPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
986
0
                                                       aFilterEntries.mbHasDates, nColWidth));
987
988
0
    int nMaxTextWidth = 0;
989
0
    if (aFilterEntries.size() <= 10)
990
0
    {
991
        // do pixel calculation for all elements of short lists
992
0
        for (const auto& rEntry : aFilterEntries)
993
0
        {
994
0
            const OUString& aText = rEntry.GetString();
995
0
            nMaxTextWidth = std::max<int>(nMaxTextWidth, mpAutoFilterPopup->GetTextWidth(aText) + aText.getLength() * 2);
996
0
        }
997
0
    }
998
0
    else
999
0
    {
1000
        // find the longest string, probably it will be the longest rendered text, too
1001
        // (performance optimization for long lists)
1002
0
        auto itMax = aFilterEntries.begin();
1003
0
        for (auto it = itMax; it != aFilterEntries.end(); ++it)
1004
0
        {
1005
0
            int nTextWidth = it->GetString().getLength();
1006
0
            if (nMaxTextWidth < nTextWidth)
1007
0
            {
1008
0
                nMaxTextWidth = nTextWidth;
1009
0
                itMax = it;
1010
0
            }
1011
0
        }
1012
0
        nMaxTextWidth = mpAutoFilterPopup->GetTextWidth(itMax->GetString()) + nMaxTextWidth * 2;
1013
0
    }
1014
1015
    // window should be at least as wide as the column, or the longest text + checkbox, scrollbar ... (it is estimated with 70 pixel now)
1016
    // window should be maximum 1024 pixel wide.
1017
0
    int nWindowWidth = std::min<int>(1024, nMaxTextWidth + 70);
1018
0
    nWindowWidth = mpAutoFilterPopup->IncreaseWindowWidthToFitText(nWindowWidth);
1019
0
    nMaxTextWidth = std::max<int>(nMaxTextWidth, nWindowWidth - 70);
1020
1021
0
    mpAutoFilterPopup->setOKAction(new AutoFilterAction(this, AutoFilterMode::Normal));
1022
0
    mpAutoFilterPopup->setPopupEndAction(
1023
0
        new AutoFilterPopupEndAction(this, ScAddress(nCol, nRow, nTab)));
1024
0
    std::unique_ptr<AutoFilterData> pData(new AutoFilterData);
1025
0
    pData->maPos = ScAddress(nCol, nRow, nTab);
1026
1027
0
    Point aPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
1028
0
    tools::Long nSizeX  = 0;
1029
0
    tools::Long nSizeY  = 0;
1030
0
    mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
1031
0
    if (bLOKActive)
1032
0
    {
1033
        // Reverse the zoom factor from aPos and nSize[X|Y]
1034
        // before letting the autofilter window convert the to twips
1035
        // with no zoom information.
1036
0
        double fZoomX(mrViewData.GetZoomX());
1037
0
        double fZoomY(mrViewData.GetZoomY());
1038
0
        aPos.setX(aPos.getX() / fZoomX);
1039
0
        aPos.setY(aPos.getY() / fZoomY);
1040
0
        nSizeX = nSizeX / fZoomX;
1041
0
        nSizeY = nSizeY / fZoomY;
1042
0
        SendAutofilterPopupPosition(nCol, nRow); // Send the position of the autofilter popup.
1043
0
    }
1044
0
    tools::Rectangle aCellRect(bLOKActive ? aPos : OutputToScreenPixel(aPos), Size(nSizeX, nSizeY));
1045
1046
0
    ScDBData* pDBData = rDoc.GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA);
1047
0
    if (!pDBData)
1048
0
        return;
1049
1050
0
    pData->mpData = pDBData;
1051
0
    mpAutoFilterPopup->setExtendedData(std::move(pData));
1052
1053
0
    ScQueryParam aParam;
1054
0
    pDBData->GetQueryParam(aParam);
1055
0
    std::vector<ScQueryEntry*> aEntries = aParam.FindAllEntriesByField(nCol);
1056
0
    std::unordered_set<OUString> aSelectedString;
1057
0
    std::unordered_set<double> aSelectedValue;
1058
0
    bool bQueryByNonEmpty = aEntries.size() == 1 && aEntries[0]->IsQueryByNonEmpty();
1059
1060
0
    if (!bQueryByNonEmpty)
1061
0
    {
1062
0
        for (ScQueryEntry* pEntry : aEntries)
1063
0
        {
1064
0
            if (pEntry && pEntry->eOp == SC_EQUAL)
1065
0
            {
1066
0
                ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
1067
0
                std::for_each(rItems.begin(), rItems.end(), AddSelectedItemString(aSelectedString, aSelectedValue));
1068
0
            }
1069
0
        }
1070
0
    }
1071
1072
    // Populate the check box list.
1073
0
    mpAutoFilterPopup->setMemberSize(aFilterEntries.size());
1074
0
    for (auto it = aFilterEntries.begin(); it != aFilterEntries.end(); ++it)
1075
0
    {
1076
        // tdf#140745 show (empty) entry on top of the checkbox list if not hidden by filter
1077
0
        if (it->GetString().isEmpty() && !it->IsHiddenByFilter())
1078
0
        {
1079
0
            const OUString& aStringVal = it->GetString();
1080
0
            const double aDoubleVal = it->GetValue();
1081
0
            bool bSelected = true;
1082
0
            if (!aSelectedValue.empty() || !aSelectedString.empty())
1083
0
                bSelected = aSelectedString.count(aStringVal) > 0;
1084
0
            else if (bQueryByNonEmpty)
1085
0
                bSelected = false;
1086
            // it->IsHiddenByFilter() is always false here so no need to evaluate it
1087
0
            mpAutoFilterPopup->addMember(aStringVal, aDoubleVal, bSelected, false);
1088
0
            aFilterEntries.maStrData.erase(it);
1089
0
            break;
1090
0
        }
1091
0
    }
1092
0
    for (const auto& rEntry : aFilterEntries)
1093
0
    {
1094
0
        const OUString& aStringVal = rEntry.GetString();
1095
0
        const double aDoubleVal = rEntry.GetValue();
1096
0
        const double aRDoubleVal = rEntry.GetRoundedValue();
1097
0
        bool bSelected = !rEntry.IsHiddenByFilter();
1098
1099
0
        if (!aSelectedValue.empty() || !aSelectedString.empty())
1100
0
        {
1101
0
            if (rEntry.GetStringType() == ScTypedStrData::Value)
1102
0
            {
1103
0
                if (aDoubleVal == aRDoubleVal)
1104
0
                    bSelected = aSelectedValue.count(aDoubleVal) > 0
1105
0
                                || aSelectedString.count(aStringVal) > 0;
1106
0
                else
1107
0
                    bSelected = aSelectedValue.count(aDoubleVal) > 0
1108
0
                                || aSelectedValue.count(aRDoubleVal) > 0
1109
0
                                || aSelectedString.count(aStringVal) > 0;
1110
0
            }
1111
0
            else
1112
0
                bSelected = aSelectedString.count(aStringVal) > 0;
1113
0
        }
1114
1115
0
        if ( rEntry.IsDate() )
1116
0
            mpAutoFilterPopup->addDateMember( aStringVal, rEntry.GetValue(), bSelected, rEntry.IsHiddenByFilter());
1117
0
        else
1118
0
            mpAutoFilterPopup->addMember( aStringVal, aRDoubleVal, bSelected, rEntry.IsHiddenByFilter(),
1119
0
                rEntry.GetStringType() == ScTypedStrData::Value );
1120
0
    }
1121
1122
    // Populate the menu.
1123
0
    mpAutoFilterPopup->addMenuItem(
1124
0
        ScResId(STR_MENU_SORT_ASC),
1125
0
        new AutoFilterAction(this, AutoFilterMode::SortAscending));
1126
0
    mpAutoFilterPopup->addMenuItem(
1127
0
        ScResId(STR_MENU_SORT_DESC),
1128
0
        new AutoFilterAction(this, AutoFilterMode::SortDescending));
1129
0
    if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_SORT_COLOR), true, true))
1130
0
        pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, false));
1131
0
    mpAutoFilterPopup->addSeparator();
1132
0
    if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_COLOR), true, true))
1133
0
        pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, true));
1134
0
    if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_CONDITION), true, false))
1135
0
    {
1136
0
        pSubMenu->addMenuItem(
1137
0
            ScResId(SCSTR_FILTER_EMPTY), new AutoFilterAction(this, AutoFilterMode::Empty));
1138
0
        pSubMenu->addMenuItem(
1139
0
            ScResId(SCSTR_FILTER_NOTEMPTY), new AutoFilterAction(this, AutoFilterMode::NonEmpty));
1140
0
        pSubMenu->addMenuItem(
1141
0
            ScResId(SCSTR_TOP10FILTER), new AutoFilterAction(this, AutoFilterMode::Top10));
1142
0
        pSubMenu->addMenuItem(
1143
0
            ScResId(SCSTR_BOTTOM10FILTER), new AutoFilterAction(this, AutoFilterMode::Bottom10));
1144
0
        pSubMenu->addSeparator();
1145
0
        pSubMenu->addMenuItem(
1146
0
            ScResId(SCSTR_STDFILTER), new AutoFilterAction(this, AutoFilterMode::Custom));
1147
0
        pSubMenu->resizeToFitMenuItems();
1148
0
    }
1149
0
    if (aEntries.size())
1150
0
        mpAutoFilterPopup->addMenuItem(
1151
0
            ScResId(SCSTR_CLEAR_FILTER), new AutoFilterAction(this, AutoFilterMode::Clear));
1152
1153
0
    mpAutoFilterPopup->initMembers(nMaxTextWidth + 20); // 20 pixel estimated for the checkbox
1154
1155
0
    ScCheckListMenuControl::Config aConfig;
1156
0
    aConfig.mbAllowEmptySet = false;
1157
0
    aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.CurrentTabForData());
1158
0
    mpAutoFilterPopup->setConfig(aConfig);
1159
0
    if (IsMouseCaptured())
1160
0
        ReleaseMouse();
1161
0
    mpAutoFilterPopup->launch(pPopupParent, aCellRect);
1162
1163
    // remember filter rules before modification
1164
0
    mpAutoFilterPopup->getResult(aSaveAutoFilterResult);
1165
1166
0
    collectUIInformation(OUString::number(nRow), OUString::number(nCol),u"AUTOFILTER"_ustr);
1167
0
}
1168
1169
void ScGridWindow::RefreshAutoFilterButton(const ScAddress& rPos)
1170
0
{
1171
0
    if (mpFilterButton)
1172
0
    {
1173
0
        bool bFilterActive = IsAutoFilterActive(rPos.Col(), rPos.Row(), rPos.Tab());
1174
0
        mpFilterButton->setHasHiddenMember(bFilterActive);
1175
0
        mpFilterButton->setPopupPressed(false);
1176
0
        mpFilterButton->draw();
1177
0
    }
1178
0
}
1179
1180
void ScGridWindow::UpdateAutoFilterFromMenu(AutoFilterMode eMode)
1181
0
{
1182
    // Terminate autofilter popup now when there is no further user input needed
1183
0
    bool bColorMode = eMode == AutoFilterMode::TextColor || eMode == AutoFilterMode::BackgroundColor;
1184
0
    if (!bColorMode)
1185
0
        mpAutoFilterPopup->terminateAllPopupMenus();
1186
1187
0
    const AutoFilterData* pData =
1188
0
        static_cast<const AutoFilterData*>(mpAutoFilterPopup->getExtendedData());
1189
1190
0
    if (!pData)
1191
0
        return;
1192
1193
0
    const ScAddress& rPos = pData->maPos;
1194
0
    ScDBData* pDBData = pData->mpData;
1195
0
    if (!pDBData)
1196
0
        return;
1197
1198
0
    ScDocument& rDoc = mrViewData.GetDocument();
1199
0
    svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
1200
0
    switch (eMode)
1201
0
    {
1202
0
        case AutoFilterMode::SortAscending:
1203
0
        case AutoFilterMode::SortDescending:
1204
0
        {
1205
0
            SCCOL nCol = rPos.Col();
1206
0
            ScSortParam aSortParam;
1207
0
            pDBData->GetSortParam(aSortParam);
1208
0
            if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
1209
                // out of bound
1210
0
                return;
1211
1212
0
            bool bHasHeader = pDBData->HasHeader();
1213
1214
0
            aSortParam.bHasHeader = bHasHeader;
1215
0
            aSortParam.bByRow = true;
1216
0
            aSortParam.bCaseSens = false;
1217
0
            aSortParam.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
1218
0
            aSortParam.aDataAreaExtras.mbCellNotes = false;
1219
0
            aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
1220
0
            aSortParam.aDataAreaExtras.mbCellFormats = true;
1221
0
            aSortParam.bInplace = true;
1222
0
            aSortParam.maKeyState[0].bDoSort = true;
1223
0
            aSortParam.maKeyState[0].nField = nCol;
1224
0
            aSortParam.maKeyState[0].bAscending = (eMode == AutoFilterMode::SortAscending);
1225
0
            aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1226
1227
0
            for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
1228
0
                aSortParam.maKeyState[i].bDoSort = false;
1229
1230
0
            mrViewData.GetViewShell()->UISort(aSortParam);
1231
0
            return;
1232
0
        }
1233
0
        case AutoFilterMode::Custom:
1234
0
        {
1235
0
            ScRange aRange;
1236
0
            pDBData->GetArea(aRange);
1237
0
            mrViewData.GetView()->MarkRange(aRange);
1238
0
            mrViewData.GetView()->SetCursor(rPos.Col(), rPos.Row());
1239
0
            mrViewData.GetDispatcher().Execute(SID_FILTER, SfxCallMode::SLOT | SfxCallMode::RECORD);
1240
0
            return;
1241
0
        }
1242
0
        default:
1243
0
            ;
1244
0
    }
1245
1246
0
    ScQueryParam aParam;
1247
0
    pDBData->GetQueryParam(aParam);
1248
1249
0
    if (eMode == AutoFilterMode::Normal)
1250
0
    {
1251
        // Do not recreate autofilter rules if there are no changes from the user
1252
0
        ScCheckListMenuControl::ResultType aResult;
1253
0
        mpAutoFilterPopup->getResult(aResult);
1254
1255
0
        if (aResult == aSaveAutoFilterResult)
1256
0
        {
1257
0
            SAL_INFO("sc.ui", "Apply autofilter to data when entries are the same");
1258
1259
0
            if (!mpAutoFilterPopup->isAllSelected())
1260
0
            {
1261
                // Apply autofilter to data
1262
0
                ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
1263
0
                pEntry->bDoQuery = true;
1264
0
                pEntry->nField = rPos.Col();
1265
0
                pEntry->eConnect = SC_AND;
1266
0
                pEntry->eOp = SC_EQUAL;
1267
0
                mrViewData.GetView()->Query(aParam, nullptr, true);
1268
0
            }
1269
1270
0
            return;
1271
0
        }
1272
0
    }
1273
1274
    // Remove old entries in auto-filter rules
1275
0
    if (!bColorMode)
1276
0
    {
1277
0
        aParam.RemoveAllEntriesByField(rPos.Col());
1278
1279
        // tdf#46184 reset filter options to default values
1280
0
        aParam.eSearchType = utl::SearchParam::SearchType::Normal;
1281
0
        aParam.bCaseSens = false;
1282
0
        aParam.bDuplicate = true;
1283
0
        aParam.bInplace = true;
1284
0
    }
1285
1286
0
    if (eMode != AutoFilterMode::Clear
1287
0
        && !(eMode == AutoFilterMode::Normal && mpAutoFilterPopup->isAllSelected()))
1288
0
    {
1289
        // Try to use the existing entry for the column (if one exists).
1290
0
        ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
1291
1292
0
        if (!pEntry)
1293
            // Something went terribly wrong!
1294
0
            return;
1295
1296
0
        if (ScTabViewShell::isAnyEditViewInRange(mrViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
1297
0
            return;
1298
1299
0
        pEntry->bDoQuery = true;
1300
0
        pEntry->nField = rPos.Col();
1301
0
        pEntry->eConnect = SC_AND;
1302
1303
0
        switch (eMode)
1304
0
        {
1305
0
            case AutoFilterMode::Normal:
1306
0
            {
1307
0
                pEntry->eOp = SC_EQUAL;
1308
1309
0
                ScCheckListMenuControl::ResultType aResult;
1310
0
                mpAutoFilterPopup->getResult(aResult);
1311
1312
0
                ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
1313
0
                rItems.clear();
1314
0
                std::for_each(aResult.begin(), aResult.end(), AddItemToEntry(rItems, rPool));
1315
0
            }
1316
0
            break;
1317
0
            case AutoFilterMode::Top10:
1318
0
                pEntry->eOp = SC_TOPVAL;
1319
0
                pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
1320
0
                pEntry->GetQueryItem().maString = rPool.intern(u"10"_ustr);
1321
0
            break;
1322
0
            case AutoFilterMode::Bottom10:
1323
0
                pEntry->eOp = SC_BOTVAL;
1324
0
                pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
1325
0
                pEntry->GetQueryItem().maString = rPool.intern(u"10"_ustr);
1326
0
            break;
1327
0
            case AutoFilterMode::Empty:
1328
0
                pEntry->SetQueryByEmpty();
1329
0
            break;
1330
0
            case AutoFilterMode::NonEmpty:
1331
0
                pEntry->SetQueryByNonEmpty();
1332
0
            break;
1333
0
            case AutoFilterMode::TextColor:
1334
0
            case AutoFilterMode::BackgroundColor:
1335
0
                assert(false && "should be handled by AutoFilterColorAction::execute");
1336
0
            break;
1337
0
            break;
1338
0
            default:
1339
                // We don't know how to handle this!
1340
0
                return;
1341
0
        }
1342
0
    }
1343
1344
0
    SendAutofilterChange();
1345
0
    mrViewData.GetView()->Query(aParam, nullptr, true);
1346
0
    pDBData->SetQueryParam(aParam);
1347
0
}
1348
1349
namespace {
1350
1351
void getCellGeometry(Point& rScrPos, Size& rScrSize, const ScViewData& rViewData, SCCOL nCol, SCROW nRow, ScSplitPos eWhich)
1352
0
{
1353
    // Get the screen position of the cell.
1354
0
    rScrPos = rViewData.GetScrPos(nCol, nRow, eWhich);
1355
1356
    // Get the screen size of the cell.
1357
0
    tools::Long nSizeX, nSizeY;
1358
0
    rViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
1359
0
    rScrSize = Size(nSizeX-1, nSizeY-1);
1360
0
}
1361
1362
}
1363
1364
void ScGridWindow::LaunchPageFieldMenu( SCCOL nCol, SCROW nRow )
1365
0
{
1366
0
    if (nCol == 0)
1367
        // We assume that the page field button is located in cell to the immediate left.
1368
0
        return;
1369
1370
0
    SCTAB nTab = mrViewData.CurrentTabForData();
1371
0
    ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
1372
0
    if (!pDPObj)
1373
0
        return;
1374
1375
0
    Point aScrPos;
1376
0
    Size aScrSize;
1377
0
    getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
1378
0
    bool bLOK = comphelper::LibreOfficeKit::isActive();
1379
0
    DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol-1, nRow, nTab), pDPObj);
1380
0
}
1381
1382
void ScGridWindow::LaunchDPFieldMenu( SCCOL nCol, SCROW nRow )
1383
0
{
1384
0
    SCTAB nTab = mrViewData.CurrentTabForData();
1385
0
    ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
1386
0
    if (!pDPObj)
1387
0
        return;
1388
1389
0
    Point aScrPos;
1390
0
    Size aScrSize;
1391
0
    getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
1392
0
    bool bLOK = comphelper::LibreOfficeKit::isActive();
1393
0
    DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol, nRow, nTab), pDPObj);
1394
0
}
1395
1396
void ScGridWindow::ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL)
1397
0
{
1398
0
    auto nSizeX = rCellRect.GetWidth();
1399
1400
    // minimum width in pixel
1401
0
    if (comphelper::LibreOfficeKit::isActive())
1402
0
    {
1403
0
        const tools::Long nMinLOKWinWidth = o3tl::convert(STD_COL_WIDTH * 13 / 10, o3tl::Length::twip, o3tl::Length::px);
1404
0
        if (nSizeX < nMinLOKWinWidth)
1405
0
            nSizeX = nMinLOKWinWidth;
1406
0
    }
1407
1408
0
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1409
0
    int nEntryCount = rFilterBox.n_children();
1410
0
    if (nEntryCount > SC_FILTERLISTBOX_LINES)
1411
0
        nEntryCount = SC_FILTERLISTBOX_LINES;
1412
0
    auto nHeight = rFilterBox.get_height_rows(nEntryCount);
1413
0
    rFilterBox.set_size_request(-1, nHeight);
1414
0
    Size aSize(rFilterBox.get_preferred_size());
1415
0
    auto nMaxToExpandTo = std::min(nSizeX, static_cast<decltype(nSizeX)>(300));     // do not over do it (Pixel)
1416
0
    if (aSize.Width() < nMaxToExpandTo)
1417
0
        aSize.setWidth(nMaxToExpandTo);
1418
1419
0
    aSize.AdjustWidth(4); // add a little margin
1420
0
    nSizeX += 4;
1421
0
    aSize.AdjustHeight(4);
1422
1423
0
    tools::Rectangle aCellRect(rCellRect);
1424
0
    aCellRect.AdjustLeft(-2); // offset the little margin above
1425
1426
0
    if (!bLayoutRTL && aSize.Width() > nSizeX)
1427
0
    {
1428
        //  move popup position
1429
0
        tools::Long nDiff = aSize.Width() - nSizeX;
1430
0
        tools::Long nNewX = aCellRect.Left() - nDiff;
1431
0
        if ( nNewX < 0 )
1432
0
            nNewX = 0;
1433
0
        aCellRect.SetLeft( nNewX );
1434
0
    }
1435
1436
0
    rFilterBox.set_size_request(aSize.Width(), aSize.Height());
1437
1438
0
    if (IsMouseCaptured())
1439
0
        ReleaseMouse();
1440
0
    mpFilterBox->popup_at_rect(pParent, aCellRect);
1441
0
}
1442
1443
void ScGridWindow::DoScenarioMenu( const ScRange& rScenRange )
1444
0
{
1445
0
    bool bMenuAtTop = true;
1446
1447
0
    ScDocument& rDoc = mrViewData.GetDocument();
1448
0
    mpFilterBox.reset();
1449
1450
0
    SCCOL nCol = rScenRange.aEnd.Col();     // Cell is below the Buttons
1451
0
    SCROW nRow = rScenRange.aStart.Row();
1452
0
    if (nRow == 0)
1453
0
    {
1454
0
        nRow = rScenRange.aEnd.Row() + 1;   // Range at very the top -> Button below
1455
0
        if (nRow>rDoc.MaxRow()) nRow = rDoc.MaxRow();
1456
0
        bMenuAtTop = false;
1457
0
    }
1458
1459
0
    SCTAB nTab = mrViewData.CurrentTabForData();
1460
0
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1461
1462
0
    tools::Long nSizeX  = 0;
1463
0
    tools::Long nSizeY  = 0;
1464
0
    mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
1465
    // The button height should not use the merged cell height, should still use single row height
1466
0
    nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
1467
0
    Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
1468
0
    if ( bLayoutRTL )
1469
0
        aPos.AdjustX( -nSizeX );
1470
0
    tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
1471
0
    aCellRect.AdjustTop( -nSizeY );
1472
0
    aCellRect.AdjustBottom( -(nSizeY - 1) );
1473
0
    if (!bMenuAtTop)
1474
0
    {
1475
0
        Size aButSize = mrViewData.GetScenButSize();
1476
0
        aCellRect.AdjustBottom(aButSize.Height());
1477
0
    }
1478
1479
    //  Place the ListBox directly below the black line of the cell grid
1480
    //  (It looks odd if the line gets hidden...)
1481
1482
0
    weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
1483
0
    mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::Scenario);
1484
0
    mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
1485
0
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1486
0
    rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
1487
1488
    //  Listbox fill
1489
0
    rFilterBox.freeze();
1490
0
    OUString aCurrent;
1491
0
    OUString aTabName;
1492
0
    SCTAB nTabCount = rDoc.GetTableCount();
1493
0
    for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
1494
0
    {
1495
0
        if (rDoc.HasScenarioRange( i, rScenRange ))
1496
0
            if (rDoc.GetName( i, aTabName ))
1497
0
            {
1498
0
                rFilterBox.append_text(aTabName);
1499
0
                if (rDoc.IsActiveScenario(i))
1500
0
                    aCurrent = aTabName;
1501
0
            }
1502
0
    }
1503
0
    rFilterBox.thaw();
1504
1505
0
    ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
1506
1507
0
    rFilterBox.grab_focus();
1508
1509
0
    sal_Int32 nPos = -1;
1510
0
    if (!aCurrent.isEmpty())
1511
0
    {
1512
0
        nPos = rFilterBox.find_text(aCurrent);
1513
0
    }
1514
0
    if (nPos == -1 && rFilterBox.n_children() > 0 )
1515
0
    {
1516
0
        nPos = 0;
1517
0
    }
1518
0
    if (nPos != -1)
1519
0
    {
1520
0
        rFilterBox.set_cursor(nPos);
1521
0
        rFilterBox.select(nPos);
1522
0
    }
1523
0
    mpFilterBox->EndInit();
1524
0
}
1525
1526
void ScGridWindow::LaunchDataSelectMenu(const SCCOL nCol, const SCROW nRow)
1527
0
{
1528
0
    mpFilterBox.reset();
1529
1530
0
    ScDocument& rDoc = mrViewData.GetDocument();
1531
0
    const SCTAB nTab = mrViewData.CurrentTabForData();
1532
0
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1533
1534
0
    tools::Long nSizeX  = 0;
1535
0
    tools::Long nSizeY  = 0;
1536
0
    mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
1537
0
    Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
1538
0
    bool bLOKActive = comphelper::LibreOfficeKit::isActive();
1539
1540
0
    if (bLOKActive)
1541
0
    {
1542
        // aPos is now view-zoom adjusted and in pixels an more importantly this is pixel aligned to the view-zoom,
1543
        // but once we use this to set the position of the floating window, it has no information of view-zoom level
1544
        // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, MapMode(MapUnit::MapTwip)) employed in
1545
        // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position which will again get zoom scaled in the
1546
        // client (effective double scaling) causing wrong positioning/size.
1547
0
        double fZoomX(mrViewData.GetZoomX());
1548
0
        double fZoomY(mrViewData.GetZoomY());
1549
0
        assert(fZoomX != 0.0 && fZoomY != 0.0 && "cannot be zero");
1550
0
        aPos.setX(aPos.getX() / fZoomX);
1551
0
        aPos.setY(aPos.getY() / fZoomY);
1552
0
        nSizeX = nSizeX / fZoomX;
1553
0
        nSizeY = nSizeY / fZoomY;
1554
0
    }
1555
1556
0
    if ( bLayoutRTL )
1557
0
        aPos.AdjustX( -nSizeX );
1558
0
    tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
1559
1560
0
    weld::Window* pParent = comphelper::LibreOfficeKit::isActive() ? GetFrameWeld() : weld::GetPopupParent(*this, aCellRect);
1561
0
    mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::DataSelect);
1562
0
    mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
1563
0
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1564
0
    rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
1565
1566
    // SetSize later
1567
1568
0
    const sal_uInt32 nIndex = rDoc.GetAttr(nCol, nRow, nTab, ATTR_VALIDDATA).GetValue();
1569
0
    const ScValidationData* pData = nIndex ? rDoc.GetValidationEntry(nIndex) : nullptr;
1570
1571
0
    bool bEmpty = false;
1572
0
    std::vector<ScTypedStrData> aStrings; // case sensitive
1573
    // Fill List
1574
0
    rDoc.GetDataEntries(nCol, nRow, nTab, aStrings, true /* bValidation */);
1575
1576
    // IsIgnoreBlank allows blank values. Don't add empty string unless "Allow Empty Cells"
1577
0
    if (pData && !pData->IsIgnoreBlank())
1578
0
    {
1579
0
        auto lambda = [](const ScTypedStrData& rStr) { return rStr.GetString().isEmpty(); };
1580
0
        std::erase_if(aStrings, lambda);
1581
0
    }
1582
1583
0
    if (aStrings.empty())
1584
0
        bEmpty = true;
1585
1586
0
    if (!bEmpty)
1587
0
    {
1588
0
        rFilterBox.freeze();
1589
1590
        // Fill Listbox
1591
0
        bool bWait = aStrings.size() > 100;
1592
1593
0
        if (bWait)
1594
0
            EnterWait();
1595
1596
0
        for (const auto& rString : aStrings)
1597
0
        {
1598
0
            const OUString& rFilterString = rString.GetString();
1599
0
            rFilterBox.append_text(rFilterString);
1600
0
        }
1601
1602
0
        if (bWait)
1603
0
            LeaveWait();
1604
1605
0
        rFilterBox.thaw();
1606
1607
0
        ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
1608
0
    }
1609
1610
0
    sal_Int32 nSelPos = -1;
1611
1612
0
    if ( nIndex )
1613
0
    {
1614
0
        if (pData)
1615
0
        {
1616
0
            std::unique_ptr<ScTypedStrData> pNew;
1617
0
            OUString aDocStr = rDoc.GetString(nCol, nRow, nTab);
1618
0
            if ( rDoc.HasValueData( nCol, nRow, nTab ) )
1619
0
            {
1620
0
                double fVal = rDoc.GetValue(ScAddress(nCol, nRow, nTab));
1621
0
                pNew.reset(new ScTypedStrData(aDocStr, fVal, fVal, ScTypedStrData::Value));
1622
0
            }
1623
0
            else
1624
0
                pNew.reset(new ScTypedStrData(aDocStr, 0.0, 0.0, ScTypedStrData::Standard));
1625
1626
0
            if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
1627
0
            {
1628
0
                auto it = std::lower_bound(aStrings.begin(), aStrings.end(), *pNew, ScTypedStrData::LessCaseSensitive());
1629
0
                if (it != aStrings.end() && ScTypedStrData::EqualCaseSensitive()(*it, *pNew))
1630
0
                    nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
1631
0
            }
1632
0
            else
1633
0
            {
1634
0
                auto it = std::find_if(aStrings.begin(), aStrings.end(), FindTypedStrData(*pNew, true));
1635
0
                if (it != aStrings.end())
1636
0
                    nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
1637
0
            }
1638
0
        }
1639
0
    }
1640
1641
    // Do not show an empty selection List:
1642
1643
0
    if ( bEmpty )
1644
0
    {
1645
0
        mpFilterBox.reset();
1646
0
    }
1647
0
    else
1648
0
    {
1649
0
        rFilterBox.grab_focus();
1650
1651
0
        if (rFilterBox.n_children())
1652
0
        {
1653
0
            if (nSelPos != -1)
1654
0
                rFilterBox.set_cursor(nSelPos);
1655
0
            else
1656
0
                rFilterBox.set_cursor(0);
1657
0
        }
1658
        // Select only after GrabFocus, so that the focus rectangle gets correct
1659
0
        if (nSelPos != -1)
1660
0
            rFilterBox.select(nSelPos);
1661
0
        else
1662
0
            rFilterBox.unselect_all();
1663
1664
0
        mpFilterBox->EndInit();
1665
0
    }
1666
0
    collectUIInformation(OUString::number(nRow), OUString::number(nCol),u"SELECTMENU"_ustr);
1667
0
}
1668
1669
void ScGridWindow::FilterSelect( sal_uLong nSel )
1670
0
{
1671
0
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1672
0
    OUString aString = rFilterBox.get_text(static_cast<sal_Int32>(nSel));
1673
1674
0
    SCCOL nCol = mpFilterBox->GetCol();
1675
0
    SCROW nRow = mpFilterBox->GetRow();
1676
0
    switch (mpFilterBox->GetMode())
1677
0
    {
1678
0
        case ScFilterBoxMode::DataSelect:
1679
0
            ExecDataSelect(nCol, nRow, aString);
1680
0
            break;
1681
0
        case ScFilterBoxMode::Scenario:
1682
0
            mrViewData.GetView()->UseScenario(aString);
1683
0
            break;
1684
0
    }
1685
1686
    // coverity[check_after_deref] - could be destroyed by ExecDataSelect
1687
0
    if (mpFilterBox)
1688
0
        mpFilterBox->popdown();
1689
1690
0
    GrabFocus();        // Otherwise the focus would be wrong on OS/2
1691
0
}
1692
1693
void ScGridWindow::ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr )
1694
0
{
1695
0
    ScInputHandler* pViewHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
1696
0
    if (pViewHdl && mrViewData.HasEditView(mrViewData.GetActivePart()))
1697
0
        pViewHdl->CancelHandler();
1698
1699
0
    SCTAB nTab = mrViewData.CurrentTabForData();
1700
0
    ScViewFunc* pView = mrViewData.GetView();
1701
0
    pView->EnterData( nCol, nRow, nTab, rStr );
1702
1703
    // #i52307# CellContentChanged is not in EnterData so it isn't called twice
1704
    // if the cursor is moved afterwards.
1705
0
    pView->CellContentChanged();
1706
0
}
1707
1708
void ScGridWindow::MoveMouseStatus( ScGridWindow& rDestWin )
1709
0
{
1710
0
    if (nButtonDown)
1711
0
    {
1712
0
        rDestWin.nButtonDown = nButtonDown;
1713
0
        rDestWin.nMouseStatus = nMouseStatus;
1714
0
    }
1715
1716
0
    if (bRFMouse)
1717
0
    {
1718
0
        rDestWin.bRFMouse = bRFMouse;
1719
0
        rDestWin.bRFSize  = bRFSize;
1720
0
        rDestWin.nRFIndex = nRFIndex;
1721
0
        rDestWin.nRFAddX  = nRFAddX;
1722
0
        rDestWin.nRFAddY  = nRFAddY;
1723
0
        bRFMouse = false;
1724
0
    }
1725
1726
0
    if (nPagebreakMouse)
1727
0
    {
1728
0
        rDestWin.nPagebreakMouse  = nPagebreakMouse;
1729
0
        rDestWin.nPagebreakBreak  = nPagebreakBreak;
1730
0
        rDestWin.nPagebreakPrev   = nPagebreakPrev;
1731
0
        rDestWin.aPagebreakSource = aPagebreakSource;
1732
0
        rDestWin.aPagebreakDrag   = aPagebreakDrag;
1733
0
        nPagebreakMouse = SC_PD_NONE;
1734
0
    }
1735
0
}
1736
1737
bool ScGridWindow::TestMouse( const MouseEvent& rMEvt, bool bAction )
1738
0
{
1739
    //  MouseEvent buttons must only be checked if bAction==TRUE
1740
    //  to allow changing the mouse pointer in MouseMove,
1741
    //  but not start AutoFill with right button (#74229#).
1742
    //  with bAction==sal_True, SetFillMode / SetDragMode is called
1743
1744
0
    if ( bAction && !rMEvt.IsLeft() )
1745
0
        return false;
1746
1747
0
    bool bNewPointer = false;
1748
1749
0
    SfxInPlaceClient* pClient = mrViewData.GetViewShell()->GetIPClient();
1750
0
    bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
1751
1752
0
    if ( mrViewData.IsActive() && !bOleActive && !mrViewData.GetViewShell()->IsLokReadOnlyView())
1753
0
    {
1754
0
        ScDocument& rDoc = mrViewData.GetDocument();
1755
0
        SCTAB nTab = mrViewData.CurrentTabForData();
1756
0
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1757
1758
        //  Auto-Fill
1759
1760
0
        ScRange aMarkRange;
1761
0
        if (mrViewData.GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE)
1762
0
        {
1763
0
            if (aMarkRange.aStart.Tab() == mrViewData.CurrentTabForData())
1764
0
            {
1765
0
                Point aMousePos = rMEvt.GetPosPixel();
1766
0
                if (mpAutoFillRect && mpAutoFillRect->Contains(aMousePos))
1767
0
                {
1768
0
                    SetPointer( PointerStyle::Cross );     //! bold cross ?
1769
0
                    if (bAction)
1770
0
                    {
1771
0
                        SCCOL nX = aMarkRange.aEnd.Col();
1772
0
                        SCROW nY = aMarkRange.aEnd.Row();
1773
1774
0
                        if ( lcl_IsEditableMatrix( mrViewData.GetDocument(), aMarkRange ) )
1775
0
                            mrViewData.SetDragMode(
1776
0
                                aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY, ScFillMode::MATRIX );
1777
0
                        else
1778
0
                            mrViewData.SetFillMode(
1779
0
                                aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY );
1780
1781
                        //  The simple selection must also be recognized when dragging,
1782
                        //  where the Marking flag is set and MarkToSimple won't work anymore.
1783
0
                        mrViewData.GetMarkData().MarkToSimple();
1784
0
                    }
1785
0
                    bNewPointer = true;
1786
0
                }
1787
0
                else if (mpDBExpandRect && mpDBExpandRect->Contains(aMousePos))
1788
0
                {
1789
0
                    SetPointer( PointerStyle::SESize );
1790
0
                    if (bAction)
1791
0
                    {
1792
0
                        SCCOL nX = maDBRange.aEnd.Col();
1793
0
                        SCROW nY = maDBRange.aEnd.Row();
1794
1795
0
                        mrViewData.SetDragMode(
1796
0
                            maDBRange.aStart.Col(), maDBRange.aStart.Row(), nX, nY, ScFillMode::DBEXPAND );
1797
0
                    }
1798
0
                    bNewPointer = true;
1799
0
                }
1800
0
            }
1801
0
        }
1802
1803
        //  Embedded rectangle
1804
1805
0
        if (rDoc.IsEmbedded())
1806
0
        {
1807
0
            ScRange aRange;
1808
0
            rDoc.GetEmbedded( aRange );
1809
0
            if ( mrViewData.CurrentTabForData() == aRange.aStart.Tab() )
1810
0
            {
1811
0
                Point aStartPos = mrViewData.GetScrPos( aRange.aStart.Col(), aRange.aStart.Row(), eWhich );
1812
0
                Point aEndPos   = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich );
1813
0
                Point aMousePos = rMEvt.GetPosPixel();
1814
0
                if ( bLayoutRTL )
1815
0
                {
1816
0
                    aStartPos.AdjustX(2 );
1817
0
                    aEndPos.AdjustX(2 );
1818
0
                }
1819
0
                bool bTop = ( aMousePos.X() >= aStartPos.X()-3 && aMousePos.X() <= aStartPos.X()+1 &&
1820
0
                              aMousePos.Y() >= aStartPos.Y()-3 && aMousePos.Y() <= aStartPos.Y()+1 );
1821
0
                bool bBottom = ( aMousePos.X() >= aEndPos.X()-3 && aMousePos.X() <= aEndPos.X()+1 &&
1822
0
                                 aMousePos.Y() >= aEndPos.Y()-3 && aMousePos.Y() <= aEndPos.Y()+1 );
1823
0
                if ( bTop || bBottom )
1824
0
                {
1825
0
                    SetPointer( PointerStyle::Cross );
1826
0
                    if (bAction)
1827
0
                    {
1828
0
                        ScFillMode nMode = bTop ? ScFillMode::EMBED_LT : ScFillMode::EMBED_RB;
1829
0
                        mrViewData.SetDragMode(
1830
0
                                    aRange.aStart.Col(), aRange.aStart.Row(),
1831
0
                                    aRange.aEnd.Col(), aRange.aEnd.Row(), nMode );
1832
0
                    }
1833
0
                    bNewPointer = true;
1834
0
                }
1835
0
            }
1836
0
        }
1837
0
    }
1838
1839
0
    if (!bNewPointer && bAction)
1840
0
    {
1841
0
        mrViewData.ResetFillMode();
1842
0
    }
1843
1844
0
    return bNewPointer;
1845
0
}
1846
1847
void ScGridWindow::MouseButtonDown( const MouseEvent& rMEvt )
1848
0
{
1849
0
    nNestedButtonState = ScNestedButtonState::Down;
1850
1851
0
    MouseEventState aState;
1852
0
    HandleMouseButtonDown(rMEvt, aState);
1853
0
    if (aState.mbActivatePart)
1854
0
        mrViewData.GetView()->ActivatePart(eWhich);
1855
1856
0
    if ( nNestedButtonState == ScNestedButtonState::Up )
1857
0
    {
1858
        // #i41690# If an object is deactivated from MouseButtonDown, it might reschedule,
1859
        // so MouseButtonUp comes before the MouseButtonDown call is finished. In this case,
1860
        // simulate another MouseButtonUp call, so the selection state is consistent.
1861
1862
0
        nButtonDown = rMEvt.GetButtons();
1863
0
        FakeButtonUp();
1864
1865
0
        if ( IsTracking() )
1866
0
            EndTracking();      // normally done in VCL as part of MouseButtonUp handling
1867
0
    }
1868
0
    nNestedButtonState = ScNestedButtonState::NONE;
1869
0
}
1870
1871
void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventState& rState )
1872
0
{
1873
    // We have to check if a context menu is shown and we have an UI
1874
    // active inplace client. In that case we have to ignore the event.
1875
    // Otherwise we would crash (context menu has been
1876
    // opened by inplace client and we would deactivate the inplace client,
1877
    // the context menu is closed by VCL asynchronously which in the end
1878
    // would work on deleted objects or the context menu has no parent anymore)
1879
0
    SfxViewShell* pViewSh = mrViewData.GetViewShell();
1880
0
    SfxInPlaceClient* pClient = pViewSh->GetIPClient();
1881
0
    if ( pClient &&
1882
0
         pClient->IsObjectInPlaceActive() &&
1883
0
         vcl::IsInPopupMenuExecute() )
1884
0
        return;
1885
1886
0
    aCurMousePos = rMEvt.GetPosPixel();
1887
1888
    // Filter popup is ended with its own mouse click, not when clicking into the Grid Window,
1889
    // so the following query is no longer necessary:
1890
0
    ClickExtern();  // deletes FilterBox when available
1891
1892
0
    HideNoteOverlay();
1893
1894
0
    bEEMouse = false;
1895
1896
0
    ScModule* pScMod = ScModule::get();
1897
0
    if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
1898
0
        return;
1899
1900
0
    const bool bWasMouseCaptured = IsMouseCaptured();
1901
0
    SAL_WARN_IF(bWasMouseCaptured, "sc.ui", "Is there a scenario where the mouse is captured before mouse down?");
1902
1903
0
    pScActiveViewShell = mrViewData.GetViewShell();         // if left is clicked
1904
0
    nScClickMouseModifier = rMEvt.GetModifier();            // to always catch a control click
1905
1906
0
    bool bDetective = mrViewData.GetViewShell()->IsAuditShell();
1907
0
    bool bRefMode = mrViewData.IsRefMode();                 // Start reference
1908
0
    bool bFormulaMode = pScMod->IsFormulaMode();            // next click -> reference
1909
0
    bool bEditMode = mrViewData.HasEditView(eWhich);        // also in Mode==SC_INPUT_TYPE
1910
0
    bool bDouble = (rMEvt.GetClicks() == 2);
1911
0
    ScDocument& rDoc = mrViewData.GetDocument();
1912
0
    bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
1913
1914
    // DeactivateIP does only happen when MarkListHasChanged
1915
1916
    // An error message can show up during GrabFocus call
1917
    // (for instance when renaming tables per sheet title)
1918
1919
0
    if ( !nButtonDown || !bDouble )             // single (first) click is always valid
1920
0
        nButtonDown = rMEvt.GetButtons();       // set nButtonDown first, so StopMarking works
1921
1922
0
    if ( ( bEditMode && mrViewData.GetActivePart() == eWhich ) || !bFormulaMode )
1923
0
        GrabFocus();
1924
1925
    // #i31846# need to cancel a double click if the first click has set the "ignore" state,
1926
    // but a single (first) click is always valid
1927
0
    if ( nMouseStatus == SC_GM_IGNORE && bDouble )
1928
0
    {
1929
0
        nButtonDown = 0;
1930
0
        nMouseStatus = SC_GM_NONE;
1931
0
        return;
1932
0
    }
1933
1934
0
    if ( bDetective )               // Detectiv fill mode
1935
0
    {
1936
0
        if ( rMEvt.IsLeft() && !rMEvt.GetModifier() )
1937
0
        {
1938
0
            Point   aPos = rMEvt.GetPosPixel();
1939
0
            SCCOL  nPosX;
1940
0
            SCROW  nPosY;
1941
0
            mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1942
1943
0
            SfxInt16Item aPosXItem( SID_RANGE_COL, nPosX );
1944
0
            SfxInt32Item aPosYItem( SID_RANGE_ROW, nPosY );
1945
0
            mrViewData.GetDispatcher().ExecuteList(SID_FILL_SELECT,
1946
0
                    SfxCallMode::SLOT | SfxCallMode::RECORD,
1947
0
                    { &aPosXItem, &aPosYItem });
1948
1949
0
        }
1950
0
        nButtonDown = 0;
1951
0
        nMouseStatus = SC_GM_NONE;
1952
0
        return;
1953
0
    }
1954
1955
0
    if (!bDouble)
1956
0
        nMouseStatus = SC_GM_NONE;
1957
1958
0
    rState.mbActivatePart = !bFormulaMode; // Don't activate when in formula mode.
1959
1960
0
    if (bFormulaMode)
1961
0
    {
1962
0
        ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
1963
0
        pSelEng->SetWindow(this);
1964
0
        pSelEng->SetWhich(eWhich);
1965
0
        pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
1966
0
    }
1967
1968
0
    if (bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNumber()))
1969
0
    {
1970
0
        Point   aPos = rMEvt.GetPosPixel();
1971
0
        SCCOL  nPosX;
1972
0
        SCROW  nPosY;
1973
0
        mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1974
1975
0
        EditView*   pEditView;
1976
0
        SCCOL       nEditCol;
1977
0
        SCROW       nEditRow;
1978
0
        mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
1979
0
        SCCOL nEndCol = mrViewData.GetEditEndCol();
1980
0
        SCROW nEndRow = mrViewData.GetEditEndRow();
1981
0
        SCCOL nStartCol = mrViewData.GetEditStartCol();
1982
1983
0
        if ( nPosX >= nStartCol && nPosX <= nEndCol &&
1984
0
             nPosY >= nEditRow && nPosY <= nEndRow )
1985
0
        {
1986
            // when clicking in the table EditView, always reset the focus
1987
0
            if (bFormulaMode)   // otherwise this has already happen above
1988
0
                GrabFocus();
1989
1990
0
            pScMod->SetInputMode( SC_INPUT_TABLE );
1991
0
            bEEMouse = true;
1992
1993
0
            if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.CurrentTabForData()))
1994
0
            {
1995
0
                Point aMouse = rMEvt.GetPosPixel();
1996
0
                tools::Rectangle aOutputArea = pEditView->GetOutputArea();
1997
0
                comphelper::ScopeGuard aOutputGuard(
1998
0
                    [pEditView, aOutputArea] {
1999
0
                        pEditView->SetOutputArea(aOutputArea);
2000
0
                    });
2001
2002
0
                lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
2003
0
                pEditView->SetOutputArea(aOutputArea);
2004
2005
0
                MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
2006
0
                                  rMEvt.GetButtons(), rMEvt.GetModifier());
2007
0
                pEditView->MouseButtonDown( aEvent );
2008
0
            }
2009
0
            else
2010
0
                pEditView->MouseButtonDown( rMEvt );
2011
0
            return;
2012
0
        }
2013
0
    }
2014
2015
0
    if (pScMod->GetIsWaterCan())
2016
0
    {
2017
        //!     what's up with the Mac ???
2018
0
        if ( rMEvt.GetModifier() + rMEvt.GetButtons() == MOUSE_RIGHT )
2019
0
        {
2020
0
            nMouseStatus = SC_GM_WATERUNDO;
2021
0
            return;
2022
0
        }
2023
0
    }
2024
2025
    // Order that matches the displayed Cursor:
2026
    // RangeFinder, AutoFill, PageBreak, Drawing
2027
2028
0
    RfCorner rCorner = NONE;
2029
0
    bool bFound = HitRangeFinder(rMEvt.GetPosPixel(), rCorner, &nRFIndex, &nRFAddX, &nRFAddY);
2030
0
    bRFSize = (rCorner != NONE);
2031
0
    aRFSelectedCorned = rCorner;
2032
2033
0
    if (bFound)
2034
0
    {
2035
0
        bRFMouse = true;        // the other variables are initialized above
2036
2037
0
        rState.mbActivatePart = true; // always activate ?
2038
0
        StartTracking();
2039
0
        return;
2040
0
    }
2041
2042
0
    bool bCrossPointer = TestMouse( rMEvt, true );
2043
0
    if ( bCrossPointer )
2044
0
    {
2045
0
        if ( bDouble )
2046
0
            mrViewData.GetView()->FillCrossDblClick();
2047
0
        else
2048
0
            pScMod->InputEnterHandler();                                // Autofill etc.
2049
0
    }
2050
2051
0
    if ( !bCrossPointer )
2052
0
    {
2053
0
        nPagebreakMouse = HitPageBreak( rMEvt.GetPosPixel(), &aPagebreakSource,
2054
0
                                            &nPagebreakBreak, &nPagebreakPrev );
2055
0
        if (nPagebreakMouse)
2056
0
        {
2057
0
            bPagebreakDrawn = false;
2058
0
            StartTracking();
2059
0
            PagebreakMove( rMEvt, false );
2060
0
            return;
2061
0
        }
2062
0
    }
2063
2064
    // in the tiled rendering case, single clicks into drawing objects take
2065
    // precedence over bEditMode
2066
0
    if (((!bFormulaMode && !bEditMode) || bIsTiledRendering) && rMEvt.IsLeft())
2067
0
    {
2068
0
        if ( !bCrossPointer && DrawMouseButtonDown(rMEvt) )
2069
0
        {
2070
0
            return;
2071
0
        }
2072
2073
0
        mrViewData.GetViewShell()->SetDrawShell( false );               // no Draw-object selected
2074
2075
        // TestMouse has already happened above
2076
0
    }
2077
2078
0
    Point aPos = rMEvt.GetPosPixel();
2079
0
    SCCOL nPosX;
2080
0
    SCROW nPosY;
2081
0
    mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2082
0
    SCTAB nTab = mrViewData.CurrentTabForData();
2083
0
    m_nDownPosX = nPosX;
2084
0
    m_nDownPosY = nPosY;
2085
2086
    // FIXME: this is to limit the number of rows handled in the Online
2087
    // to 1000; this will be removed again when the performance
2088
    // bottlenecks are sorted out
2089
0
    if ( comphelper::LibreOfficeKit::isActive() && nPosY > MAXTILEDROW - 1 )
2090
0
    {
2091
0
        nButtonDown = 0;
2092
0
        nMouseStatus = SC_GM_NONE;
2093
0
        return;
2094
0
    }
2095
2096
    // Auto filter / pivot table / data select popup.  This shouldn't activate the part.
2097
2098
0
    if ( !bDouble && !bFormulaMode && rMEvt.IsLeft() )
2099
0
    {
2100
0
        SCCOL nRealPosX;
2101
0
        SCROW nRealPosY;
2102
0
        mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nRealPosX, nRealPosY, false );//the real row/col
2103
2104
0
        bool bAutoFilterDisable = false;
2105
0
        bool bPivotDisable = false;
2106
2107
0
        if (rDoc.IsTabProtected(nTab))
2108
0
        {
2109
0
            const ScTableProtection* pTabProtection = rDoc.GetTabProtection(nTab);
2110
0
            bAutoFilterDisable = pTabProtection && !pTabProtection->isOptionEnabled(ScTableProtection::AUTOFILTER);//autofilter
2111
0
            bPivotDisable = pTabProtection && !pTabProtection->isOptionEnabled(ScTableProtection::PIVOT_TABLES);//pivot
2112
0
        }
2113
2114
        // show in the merged cells the filter of the first cell (nPosX instead of nRealPosX)
2115
0
        const ScMergeFlagAttr& rRealPosAttr = rDoc.GetAttr(nPosX, nRealPosY, nTab, ATTR_MERGE_FLAG);
2116
2117
0
        if (!bAutoFilterDisable && rRealPosAttr.HasAutoFilter())
2118
0
        {
2119
0
            pScMod->InputEnterHandler();
2120
0
            if (DoAutoFilterButton(nPosX, nRealPosY, rMEvt))
2121
0
                return;
2122
0
        }
2123
2124
0
        const ScMergeFlagAttr& rAttr = rDoc.GetAttr(nPosX, nPosY, nTab, ATTR_MERGE_FLAG);
2125
0
        if (!bAutoFilterDisable && rAttr.HasAutoFilter())
2126
0
        {
2127
0
            if (DoAutoFilterButton(nPosX, nPosY, rMEvt))
2128
0
            {
2129
0
                rState.mbActivatePart = false;
2130
0
                return;
2131
0
            }
2132
0
        }
2133
2134
0
        if (!bPivotDisable && (rAttr.HasPivotButton() || rAttr.HasPivotPopupButton() ||
2135
0
            rAttr.HasPivotMultiFieldPopupButton()))
2136
0
        {
2137
0
            DoPushPivotButton(nPosX, nPosY, rMEvt, rAttr.HasPivotButton(),
2138
0
                rAttr.HasPivotPopupButton(), rAttr.HasPivotMultiFieldPopupButton());
2139
0
            rState.mbActivatePart = false;
2140
0
            return;
2141
0
        }
2142
2143
0
        if (!bPivotDisable && rAttr.HasPivotToggle())
2144
0
        {
2145
0
            DoPushPivotToggle(nPosX, nPosY, rMEvt);
2146
0
            rState.mbActivatePart = false;
2147
0
        }
2148
2149
        //  List Validity drop-down button
2150
2151
0
        if ( bListValButton )
2152
0
        {
2153
0
            tools::Rectangle aButtonRect = GetListValButtonRect( aListValPos );
2154
0
            if ( aButtonRect.Contains( aPos ) )
2155
0
            {
2156
                // tdf#149609 if we captured the mouse in the course of this function
2157
                // release it before showing the data select menu to undo any unhelpful
2158
                // seleng capture
2159
0
                if (!bWasMouseCaptured && IsMouseCaptured())
2160
0
                    ReleaseMouse();
2161
2162
0
                const bool lokReadOnly = comphelper::LibreOfficeKit::isActive() && pViewSh->IsLokReadOnlyView();
2163
0
                if (lokReadOnly)
2164
0
                    return; // Return as if the action was performed, so the flow is not affected.
2165
2166
0
                LaunchDataSelectMenu( aListValPos.Col(), aListValPos.Row() );
2167
2168
0
                nMouseStatus = SC_GM_FILTER;    // not set in DoAutoFilterMenue for bDataSelect
2169
0
                rState.mbActivatePart = false;
2170
0
                return;
2171
0
            }
2172
0
        }
2173
0
    }
2174
2175
            //      scenario selection
2176
2177
0
    ScRange aScenRange;
2178
0
    if ( rMEvt.IsLeft() && HasScenarioButton( aPos, aScenRange ) )
2179
0
    {
2180
        // tdf#149609 if we captured the mouse in the course of this function
2181
        // release it before showing the data scenario menu to undo any unhelpful
2182
        // seleng capture
2183
0
        if (!bWasMouseCaptured && IsMouseCaptured())
2184
0
            ReleaseMouse();
2185
2186
0
        DoScenarioMenu( aScenRange );
2187
2188
        // Scenario selection comes from MouseButtonDown:
2189
        // The next MouseMove on the FilterBox is like a ButtonDown
2190
0
        nMouseStatus = SC_GM_FILTER;
2191
0
        return;
2192
0
    }
2193
2194
            //      double click started ?
2195
2196
    // StopMarking can be called from DrawMouseButtonDown
2197
2198
0
    if ( nMouseStatus != SC_GM_IGNORE && !bRefMode )
2199
0
    {
2200
0
        if ( bDouble && !bCrossPointer )
2201
0
        {
2202
0
            if (nMouseStatus == SC_GM_TABDOWN)
2203
0
                nMouseStatus = SC_GM_DBLDOWN;
2204
0
        }
2205
0
        else
2206
0
            nMouseStatus = SC_GM_TABDOWN;
2207
0
    }
2208
2209
            //      links in the edit cell
2210
2211
0
    bool bAlt = rMEvt.IsMod2();
2212
0
    if ( !bAlt && rMEvt.IsLeft() && ScGlobal::ShouldOpenURL() &&
2213
0
            GetEditUrl(rMEvt.GetPosPixel()) )           // click on link: do not move cursor
2214
0
    {
2215
0
        SetPointer( PointerStyle::RefHand );
2216
0
        nMouseStatus = SC_GM_URLDOWN;                   // also only execute when ButtonUp
2217
0
        return;
2218
0
    }
2219
2220
            //      Gridwin - Selection Engine
2221
2222
0
    if ( !rMEvt.IsLeft() )
2223
0
        return;
2224
2225
0
    ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
2226
0
    pSelEng->SetWindow(this);
2227
0
    pSelEng->SetWhich(eWhich);
2228
0
    pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
2229
2230
    //  SelMouseButtonDown on the View is still setting the bMoveIsShift flag
2231
0
    if ( mrViewData.GetView()->SelMouseButtonDown( rMEvt ) )
2232
0
    {
2233
0
        if (IsMouseCaptured())
2234
0
        {
2235
            //  Tracking instead of CaptureMouse, so it can be canceled cleanly
2236
            //! Someday SelectionEngine should call StartTracking on its own!?!
2237
0
            ReleaseMouse();
2238
0
            StartTracking();
2239
0
        }
2240
0
        mrViewData.GetMarkData().SetMarking(true);
2241
0
        return;
2242
0
    }
2243
0
}
2244
2245
void ScGridWindow::MouseButtonUp( const MouseEvent& rMEvt )
2246
0
{
2247
0
    aCurMousePos = rMEvt.GetPosPixel();
2248
0
    ScDocument& rDoc = mrViewData.GetDocument();
2249
0
    ScMarkData& rMark = mrViewData.GetMarkData();
2250
    // #i41690# detect a MouseButtonUp call from within MouseButtonDown
2251
    // (possible through Reschedule from storing an OLE object that is deselected)
2252
2253
0
    if ( nNestedButtonState == ScNestedButtonState::Down )
2254
0
        nNestedButtonState = ScNestedButtonState::Up;
2255
2256
0
    if (nButtonDown != rMEvt.GetButtons())
2257
0
        nMouseStatus = SC_GM_IGNORE;            // reset and return
2258
2259
0
    nButtonDown = 0;
2260
2261
0
    if (nMouseStatus == SC_GM_IGNORE)
2262
0
    {
2263
0
        nMouseStatus = SC_GM_NONE;
2264
                                        // Selection engine: cancel selection
2265
0
        mrViewData.GetView()->GetSelEngine()->Reset();
2266
0
        rMark.SetMarking(false);
2267
0
        if (mrViewData.IsAnyFillMode())
2268
0
        {
2269
0
            mrViewData.GetView()->StopRefMode();
2270
0
            mrViewData.ResetFillMode();
2271
0
        }
2272
0
        StopMarking();
2273
0
        DrawEndAction();                // cancel selection/moving in drawing layer
2274
0
        ReleaseMouse();
2275
0
        return;
2276
0
    }
2277
2278
0
    if (nMouseStatus == SC_GM_FILTER)
2279
0
    {
2280
0
        nMouseStatus = SC_GM_NONE;
2281
0
        ReleaseMouse();
2282
0
        return;                         // nothing more should happen here
2283
0
    }
2284
2285
0
    ScModule* pScMod = ScModule::get();
2286
0
    if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
2287
0
        return;
2288
2289
0
    SfxBindings& rBindings = mrViewData.GetBindings();
2290
0
    const bool bHasEditView(mrViewData.HasEditView( eWhich ));
2291
0
    if (bEEMouse && bHasEditView)
2292
0
    {
2293
0
        EditView*   pEditView;
2294
0
        SCCOL       nEditCol;
2295
0
        SCROW       nEditRow;
2296
0
        mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2297
2298
0
        if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.CurrentTabForData()))
2299
0
        {
2300
0
            Point aMouse = rMEvt.GetPosPixel();
2301
0
            tools::Rectangle aOutputArea = pEditView->GetOutputArea();
2302
0
            comphelper::ScopeGuard aOutputGuard(
2303
0
                [pEditView, aOutputArea] {
2304
0
                    pEditView->SetOutputArea(aOutputArea);
2305
0
                });
2306
2307
0
            lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
2308
0
            pEditView->SetOutputArea(aOutputArea);
2309
2310
0
            MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
2311
0
                              rMEvt.GetButtons(), rMEvt.GetModifier());
2312
0
            pEditView->MouseButtonUp( aEvent );
2313
0
        }
2314
0
        else
2315
0
            pEditView->MouseButtonUp( rMEvt );
2316
2317
0
        if ( rMEvt.IsMiddle() &&
2318
0
                 GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
2319
0
        {
2320
            //  EditView may have pasted from selection
2321
0
            pScMod->InputChanged( pEditView );
2322
0
        }
2323
0
        else
2324
0
            pScMod->InputSelection( pEditView );            // parentheses etc.
2325
2326
0
        mrViewData.GetView()->InvalidateAttribs();
2327
0
        rBindings.Invalidate( SID_HYPERLINK_GETLINK );
2328
0
        bEEMouse = false;
2329
0
        return;
2330
0
    }
2331
2332
0
    if (!bHasEditView)
2333
0
    {
2334
0
        SdrView* pSdrView(mrViewData.GetView()->GetScDrawView());
2335
0
        if (nullptr != pSdrView)
2336
0
        {
2337
0
            const Point aDocPos(PixelToLogic(aCurMousePos));
2338
0
            SdrHdl* pHdl(pSdrView->PickHandle(aDocPos));
2339
2340
0
            if (nullptr != pHdl)
2341
0
            {
2342
0
                SdrObject* pSingleObj(nullptr);
2343
0
                const SdrMarkList& rMarkList(pSdrView->GetMarkedObjectList());
2344
2345
0
                if (1 == rMarkList.GetMarkCount())
2346
0
                    pSingleObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
2347
2348
0
                if (nullptr != pSingleObj)
2349
0
                {
2350
                    // Check for click on svx::diagram::DiagramFrameHdl
2351
                    // - if we hit a SdrHdl
2352
                    // - if single object is selected
2353
                    //   - and it is a Diagram
2354
0
                    if(pHdl && nullptr != pSingleObj && pSingleObj->isDiagram())
2355
0
                    {
2356
0
                        svx::diagram::DiagramFrameHdl* pDiagramFrameHdl(dynamic_cast<svx::diagram::DiagramFrameHdl*>(pHdl));
2357
0
                        if(nullptr != pDiagramFrameHdl)
2358
0
                        {
2359
                            // let the DiagramFrameHdl decide what to do
2360
0
                            svx::diagram::DiagramFrameHdl::clicked(aDocPos);
2361
0
                        }
2362
0
                    }
2363
0
                }
2364
0
            }
2365
0
        }
2366
0
    }
2367
2368
0
    if (bDPMouse)
2369
0
    {
2370
0
        DPMouseButtonUp( rMEvt );       // resets bDPMouse
2371
0
        return;
2372
0
    }
2373
2374
0
    if (bRFMouse)
2375
0
    {
2376
0
        RFMouseMove( rMEvt, true );     // Again the proper range
2377
0
        bRFMouse = false;
2378
0
        SetPointer( PointerStyle::Arrow );
2379
0
        ReleaseMouse();
2380
0
        return;
2381
0
    }
2382
2383
0
    if (nPagebreakMouse)
2384
0
    {
2385
0
        PagebreakMove( rMEvt, true );
2386
0
        nPagebreakMouse = SC_PD_NONE;
2387
0
        SetPointer( PointerStyle::Arrow );
2388
0
        ReleaseMouse();
2389
0
        return;
2390
0
    }
2391
2392
0
    if (nMouseStatus == SC_GM_WATERUNDO)    // Undo in format paintbrush mode
2393
0
    {
2394
0
        SfxUndoManager* pMgr = mrViewData.GetDocShell()->GetUndoManager();
2395
0
        if ( pMgr->GetUndoActionCount() && dynamic_cast<ScUndoSelectionStyle*>(pMgr->GetUndoAction()) )
2396
0
            pMgr->Undo();
2397
0
        return;
2398
0
    }
2399
2400
0
    if (DrawMouseButtonUp(rMEvt))       // includes format paint brush handling for drawing objects
2401
0
    {
2402
0
        ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2403
0
        SfxBindings& rFrmBindings=pViewShell->GetViewFrame().GetBindings();
2404
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_WIDTH);
2405
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_HEIGHT);
2406
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
2407
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
2408
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ANGLE);
2409
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_X);
2410
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_Y);
2411
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH);
2412
0
        rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT);
2413
0
        return;
2414
0
    }
2415
2416
0
    rMark.SetMarking(false);
2417
2418
0
    SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
2419
2420
0
    if (mrViewData.IsFillMode() ||
2421
0
        ( mrViewData.GetFillMode() == ScFillMode::MATRIX && rMEvt.IsMod1() ))
2422
0
    {
2423
0
        nScFillModeMouseModifier = rMEvt.GetModifier();
2424
0
        SCCOL nStartCol;
2425
0
        SCROW nStartRow;
2426
0
        SCCOL nEndCol;
2427
0
        SCROW nEndRow;
2428
0
        mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2429
0
        ScRange aDelRange;
2430
0
        bool bIsDel = mrViewData.GetDelMark( aDelRange );
2431
2432
0
        ScViewFunc* pView = mrViewData.GetView();
2433
0
        pView->StopRefMode();
2434
0
        mrViewData.ResetFillMode();
2435
0
        pView->GetFunctionSet().SetAnchorFlag( false );    // #i5819# don't use AutoFill anchor flag for selection
2436
2437
0
        if ( bIsDel )
2438
0
        {
2439
0
            pView->MarkRange( aDelRange, false );
2440
0
            pView->DeleteContents( InsertDeleteFlags::CONTENTS );
2441
0
            SCTAB nTab = mrViewData.CurrentTabForData();
2442
0
            ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
2443
0
            if ( aBlockRange != aDelRange )
2444
0
            {
2445
0
                if ( aDelRange.aStart.Row() == nStartRow )
2446
0
                    aBlockRange.aEnd.SetCol( aDelRange.aStart.Col() - 1 );
2447
0
                else
2448
0
                    aBlockRange.aEnd.SetRow( aDelRange.aStart.Row() - 1 );
2449
0
                pView->MarkRange( aBlockRange, false );
2450
0
            }
2451
0
        }
2452
0
        else
2453
0
        {
2454
0
            mrViewData.GetDispatcher().Execute( FID_FILL_AUTO, SfxCallMode::SLOT | SfxCallMode::RECORD );
2455
2456
0
            if (comphelper::LibreOfficeKit::isActive())
2457
0
            {
2458
                // prepare AutoFill menu items for "Copy Cells" and "Fill Series"
2459
0
                ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2460
0
                boost::property_tree::ptree aMenu;
2461
0
                boost::property_tree::ptree aItemTree;
2462
2463
0
                aItemTree.put("text", "~Copy Cells");
2464
0
                aItemTree.put("type", "command");
2465
0
                aItemTree.put("command", ".uno:AutoFill?Copy:bool=true");
2466
0
                aItemTree.put("enabled", "true");
2467
0
                aMenu.push_back(std::make_pair("", aItemTree));
2468
2469
0
                aItemTree.put("text", "~Fill Series");
2470
0
                aItemTree.put("type", "command");
2471
0
                aItemTree.put("command", ".uno:AutoFill?Copy:bool=false");
2472
0
                aItemTree.put("enabled", "true");
2473
0
                aMenu.push_back(std::make_pair("", aItemTree));
2474
0
                aItemTree.clear();
2475
2476
0
                boost::property_tree::ptree aRoot;
2477
0
                aRoot.add_child("menu", aMenu);
2478
2479
0
                std::stringstream aStream;
2480
0
                boost::property_tree::write_json(aStream, aRoot, true);
2481
2482
0
                pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU,
2483
0
                                                       OString(aStream.str()));
2484
0
            }
2485
0
        }
2486
0
    }
2487
0
    else if (mrViewData.GetFillMode() == ScFillMode::MATRIX)
2488
0
    {
2489
0
        SCTAB nTab = mrViewData.CurrentTabForData();
2490
0
        SCCOL nStartCol;
2491
0
        SCROW nStartRow;
2492
0
        SCCOL nEndCol;
2493
0
        SCROW nEndRow;
2494
0
        mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2495
0
        ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
2496
0
        SCCOL nFillCol = mrViewData.GetRefEndX();
2497
0
        SCROW nFillRow = mrViewData.GetRefEndY();
2498
0
        ScAddress aEndPos( nFillCol, nFillRow, nTab );
2499
2500
0
        ScTabView* pView = mrViewData.GetView();
2501
0
        pView->StopRefMode();
2502
0
        mrViewData.ResetFillMode();
2503
0
        pView->GetFunctionSet().SetAnchorFlag( false );
2504
2505
0
        if ( aEndPos != aBlockRange.aEnd )
2506
0
        {
2507
0
            mrViewData.GetDocShell()->GetDocFunc().ResizeMatrix( aBlockRange, aEndPos );
2508
0
            mrViewData.GetView()->MarkRange( ScRange( aBlockRange.aStart, aEndPos ) );
2509
0
        }
2510
0
    }
2511
0
    else if (mrViewData.IsDBExpandMode())
2512
0
    {
2513
0
        SCCOL nStartCol;
2514
0
        SCROW nStartRow;
2515
0
        SCCOL nEndCol;
2516
0
        SCROW nEndRow;
2517
0
        mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2518
0
        SCCOL nFillCol = mrViewData.GetRefEndX();
2519
0
        SCROW nFillRow = mrViewData.GetRefEndY();
2520
0
        ScViewFunc* pView = mrViewData.GetView();
2521
0
        pView->StopRefMode();
2522
0
        mrViewData.ResetFillMode();
2523
0
        pView->GetFunctionSet().SetAnchorFlag( false );    // #i5819# don't use AutoFill anchor flag for selection
2524
0
        ScAddress aCurrentAddress = mrViewData.GetCurPos();
2525
0
        ScDocument& rDocument = mrViewData.GetDocument();
2526
0
        if (ScDBData* pDBData = rDocument.GetTableDBAtCursor(aCurrentAddress.Col(), aCurrentAddress.Row(), aCurrentAddress.Tab(), ScDBDataPortion::AREA))
2527
0
        {
2528
0
            SCTAB nTab = mrViewData.GetTabNumber();
2529
0
            ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
2530
0
            ScDBData aNewDBData(*pDBData);
2531
0
            ScRange aNewDBRange(nStartCol, nStartRow, nTab, nFillCol, nFillRow, nTab);
2532
0
            aNewDBRange.PutInOrder();
2533
0
            aNewDBData.SetArea(nTab, aNewDBRange.aStart.Col(), aNewDBRange.aStart.Row(), aNewDBRange.aEnd.Col(), aNewDBRange.aEnd.Row());
2534
            // Do subtotal if needed
2535
0
            ScRange aOldRange;
2536
0
            pDBData->GetArea(aOldRange);
2537
0
            if (aNewDBData.HasTotals() && (aOldRange.aEnd.Row() != aNewDBRange.aEnd.Row() || aOldRange.aStart.Row() != aNewDBRange.aStart.Row()))
2538
0
            {
2539
                // Subtotals
2540
0
                ScSubTotalParam aSubTotalParam;
2541
0
                pDBData->GetSubTotalParam(aSubTotalParam);
2542
0
                aSubTotalParam.bHasHeader = aNewDBData.HasHeader();
2543
                // store current subtotal settings
2544
0
                pDBData->CreateTotalRowParam(aSubTotalParam);
2545
0
                aNewDBData.SetSubTotalParam(aSubTotalParam);
2546
                // add/replace total row
2547
0
                aSubTotalParam.bRemoveOnly = false;
2548
0
                aSubTotalParam.bReplace = true;
2549
0
                aFunc.DoTableSubTotals(aNewDBData.GetTab(), aNewDBData, aSubTotalParam, true, false);
2550
0
            }
2551
0
            else
2552
0
            {
2553
0
                aFunc.ModifyDBData(aNewDBData);
2554
0
            }
2555
0
        }
2556
0
    }
2557
0
    else if (mrViewData.IsAnyFillMode())
2558
0
    {
2559
                                                // Embedded area has been changed
2560
0
        ScTabView* pView = mrViewData.GetView();
2561
0
        pView->StopRefMode();
2562
0
        mrViewData.ResetFillMode();
2563
0
        pView->GetFunctionSet().SetAnchorFlag( false );
2564
0
        mrViewData.GetDocShell()->UpdateOle(mrViewData);
2565
0
    }
2566
2567
0
    bool bRefMode = mrViewData.IsRefMode();
2568
0
    if (bRefMode)
2569
0
        pScMod->EndReference();
2570
2571
        // Format paintbrush mode (Switch)
2572
2573
0
    if (pScMod->GetIsWaterCan())
2574
0
    {
2575
        // Check on undo already done above
2576
2577
0
        ScStyleSheetPool* pStylePool = mrViewData.GetDocument().
2578
0
                                            GetStyleSheetPool();
2579
0
        if ( pStylePool )
2580
0
        {
2581
0
            SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(
2582
0
                                         pStylePool->GetActualStyleSheet());
2583
2584
0
            if ( pStyleSheet )
2585
0
            {
2586
0
                SfxStyleFamily eFamily = pStyleSheet->GetFamily();
2587
2588
0
                switch ( eFamily )
2589
0
                {
2590
0
                    case SfxStyleFamily::Para:
2591
0
                        mrViewData.GetView()->SetStyleSheetToMarked( pStyleSheet );
2592
0
                        mrViewData.GetView()->DoneBlockMode();
2593
0
                        break;
2594
2595
0
                    case SfxStyleFamily::Page:
2596
0
                        mrViewData.GetDocument().SetPageStyle( mrViewData.CurrentTabForData(),
2597
0
                                                               pStyleSheet->GetName() );
2598
2599
0
                        ScPrintFunc( *mrViewData.GetDocShell(),
2600
0
                                     mrViewData.GetViewShell()->GetPrinter(true),
2601
0
                                     mrViewData.CurrentTabForData() ).UpdatePages();
2602
2603
0
                        rBindings.Invalidate( SID_STATUS_PAGESTYLE );
2604
0
                        break;
2605
2606
0
                    default:
2607
0
                        break;
2608
0
                }
2609
0
            }
2610
0
        }
2611
0
    }
2612
2613
0
    ScDBFunc* pView = mrViewData.GetView();
2614
0
    ScDocument* pBrushDoc = pView->GetBrushDocument();
2615
0
    if ( pBrushDoc )
2616
0
    {
2617
0
        pView->PasteFromClip( InsertDeleteFlags::ATTRIB, pBrushDoc );
2618
0
        if ( !pView->IsPaintBrushLocked() )
2619
0
            pView->ResetBrushDocument();            // invalidates pBrushDoc pointer
2620
0
    }
2621
2622
0
    Point aPos = rMEvt.GetPosPixel();
2623
0
    SCCOL nPosX;
2624
0
    SCROW nPosY;
2625
0
    SCTAB nTab = mrViewData.CurrentTabForData();
2626
0
    mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2627
0
    ScDPObject* pDPObj  = rDoc.GetDPAtCursor( nPosX, nPosY, nTab );
2628
2629
    // double click (only left button)
2630
2631
0
    bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
2632
0
    if ( (rMEvt.GetClicks() == 2 && rMEvt.IsLeft())
2633
0
            && !bRefMode
2634
0
            && (nMouseStatus == SC_GM_DBLDOWN || (bIsTiledRendering && nMouseStatus != SC_GM_URLDOWN))
2635
0
            && !pScMod->IsRefDialogOpen())
2636
0
    {
2637
        //  data pilot table
2638
0
        if ( pDPObj && pDPObj->GetSaveData()->GetDrillDown() )
2639
0
        {
2640
0
            ScAddress aCellPos( nPosX, nPosY, mrViewData.CurrentTabForData() );
2641
2642
            // Check for header drill-down first.
2643
0
            sheet::DataPilotTableHeaderData aData;
2644
0
            pDPObj->GetHeaderPositionData(aCellPos, aData);
2645
2646
0
            if ( ( aData.Flags & sheet::MemberResultFlags::HASMEMBER ) &&
2647
0
                 ! ( aData.Flags & sheet::MemberResultFlags::SUBTOTAL ) )
2648
0
            {
2649
0
                css::sheet::DataPilotFieldOrientation nDummy;
2650
0
                if ( pView->HasSelectionForDrillDown( nDummy ) )
2651
0
                {
2652
                    // execute slot to show dialog
2653
0
                    mrViewData.GetDispatcher().Execute( SID_OUTLINE_SHOW, SfxCallMode::SLOT | SfxCallMode::RECORD );
2654
0
                }
2655
0
                else
2656
0
                {
2657
                    // toggle single entry
2658
0
                    ScDPObject aNewObj( *pDPObj );
2659
0
                    pDPObj->ToggleDetails( aData, &aNewObj );
2660
0
                    ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
2661
0
                    aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
2662
0
                    mrViewData.GetView()->CursorPosChanged();       // shells may be switched
2663
0
                }
2664
0
            }
2665
0
            else
2666
0
            {
2667
                // Check if the data area is double-clicked.
2668
2669
0
                Sequence<sheet::DataPilotFieldFilter> aFilters;
2670
0
                if ( pDPObj->GetDataFieldPositionData(aCellPos, aFilters) )
2671
0
                    mrViewData.GetView()->ShowDataPilotSourceData( *pDPObj, aFilters );
2672
0
            }
2673
2674
0
            return;
2675
0
        }
2676
2677
        // Check for cell protection attribute.
2678
0
        const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
2679
0
        bool bEditAllowed = true;
2680
0
        if ( pProtect && pProtect->isProtected() )
2681
0
        {
2682
0
            bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
2683
0
            bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
2684
0
            bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
2685
2686
0
            if ( bSkipProtected && bSkipUnprotected )
2687
0
                bEditAllowed = false;
2688
0
            else if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
2689
0
                bEditAllowed = false;
2690
0
        }
2691
2692
0
        if ( bEditAllowed )
2693
0
        {
2694
            //  edit cell contents
2695
0
            mrViewData.GetViewShell()->UpdateInputHandler();
2696
0
            pScMod->SetInputMode( SC_INPUT_TABLE );
2697
0
            if (mrViewData.HasEditView(eWhich))
2698
0
            {
2699
                // Set text cursor where clicked
2700
0
                EditView* pEditView = mrViewData.GetEditView( eWhich );
2701
0
                MouseEvent aEditEvt( rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 );
2702
0
                pEditView->MouseButtonDown( aEditEvt );
2703
0
                pEditView->MouseButtonUp( aEditEvt );
2704
0
            }
2705
0
        }
2706
2707
0
        if ( bIsTiledRendering && rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt ) )
2708
0
        {
2709
0
            mrViewData.GetView()->SelectionChanged();
2710
0
        }
2711
2712
0
        return;
2713
0
    }
2714
2715
            //      Links in edit cells
2716
2717
0
    bool bAlt = rMEvt.IsMod2();
2718
0
    if ( !bAlt && !bRefMode && nMouseStatus == SC_GM_URLDOWN )
2719
0
    {
2720
        //  Only execute on ButtonUp, if ButtonDown also was done on a URL
2721
2722
0
        OUString aName, aUrl, aTarget;
2723
0
        SCCOL nUrlCellX;
2724
0
        if (GetEditUrl(rMEvt.GetPosPixel(), &aName, &aUrl, &aTarget, &nUrlCellX))
2725
0
        {
2726
0
            nMouseStatus = SC_GM_NONE;              // Ignore double-click
2727
0
            bool isTiledRendering = comphelper::LibreOfficeKit::isActive();
2728
            // ScGlobal::OpenURL() only understands Calc A1 style syntax.
2729
            // Convert it to Calc A1 before calling OpenURL().
2730
0
            if (rDoc.GetAddressConvention() == formula::FormulaGrammar::CONV_OOO)
2731
0
            {
2732
0
                if (aUrl.startsWith("#")) {
2733
0
                        ScGlobal::OpenURL(aUrl, aTarget, isTiledRendering);
2734
0
                        return;
2735
0
                }
2736
0
                ScGlobal::OpenURL(aUrl, aTarget);
2737
0
            }
2738
0
            else
2739
0
            {
2740
0
                ScAddress aTempAddr;
2741
0
                ScAddress::ExternalInfo aExtInfo;
2742
0
                ScRefFlags nRes = aTempAddr.Parse(aUrl, rDoc, rDoc.GetAddressConvention(), &aExtInfo);
2743
0
                if (!(nRes & ScRefFlags::VALID))
2744
0
                {
2745
                    // Not a reference string. Pass it through unmodified.
2746
0
                    ScGlobal::OpenURL(aUrl, aTarget);
2747
0
                    return;
2748
0
                }
2749
2750
0
                OUStringBuffer aBuf;
2751
0
                if (aExtInfo.mbExternal)
2752
0
                {
2753
                    // External reference.
2754
0
                    ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
2755
0
                    const OUString* pStr = pRefMgr->getExternalFileName(aExtInfo.mnFileId);
2756
0
                    if (pStr)
2757
0
                        aBuf.append(*pStr);
2758
2759
0
                    OUString aRefCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS, nullptr, formula::FormulaGrammar::CONV_OOO));
2760
0
                    aBuf.append("#" + aExtInfo.maTabName + "." + aRefCalcA1);
2761
0
                    ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget);
2762
0
                }
2763
0
                else
2764
0
                {
2765
                    // Internal reference.
2766
0
                    OUString aUrlCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, formula::FormulaGrammar::CONV_OOO));
2767
0
                    aBuf.append("#" + aUrlCalcA1);
2768
0
                    ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget, isTiledRendering);
2769
0
                }
2770
0
            }
2771
2772
            // fire worksheet_followhyperlink event
2773
0
            uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = rDoc.GetVbaEventProcessor();
2774
0
            if( xVbaEvents.is() ) try
2775
0
            {
2776
0
                aPos = rMEvt.GetPosPixel();
2777
0
                nTab = mrViewData.CurrentTabForData();
2778
0
                mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2779
0
                OUString sURL;
2780
0
                ScRefCellValue aCell;
2781
0
                if (lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
2782
0
                {
2783
0
                    ScAddress aCellPos( nPosX, nPosY, nTab );
2784
0
                    uno::Reference< table::XCell > xCell( new ScCellObj( mrViewData.GetDocShell(), aCellPos ) );
2785
0
                    uno::Sequence< uno::Any > aArgs{ uno::Any(xCell) };
2786
0
                    xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_FOLLOWHYPERLINK, aArgs );
2787
0
                }
2788
0
            }
2789
0
            catch( uno::Exception& )
2790
0
            {
2791
0
            }
2792
2793
0
            return;
2794
0
        }
2795
0
    }
2796
2797
            //      Gridwin - SelectionEngine
2798
2799
    //  SelMouseButtonDown is called only for left button, but SelMouseButtonUp would return
2800
    //  sal_True for any call, so IsLeft must be checked here, too.
2801
2802
0
    if ( !(rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt )) )
2803
0
        return;
2804
2805
0
    mrViewData.GetView()->SelectionChanged();
2806
2807
0
    SfxDispatcher* pDisp = mrViewData.GetViewShell()->GetDispatcher();
2808
0
    bool bFormulaMode = pScMod->IsFormulaMode();
2809
0
    OSL_ENSURE( pDisp || bFormulaMode, "Cursor moved on inactive View ?" );
2810
2811
    //  #i14927# execute SID_CURRENTCELL (for macro recording) only if there is no
2812
    //  multiple selection, so the argument string completely describes the selection,
2813
    //  and executing the slot won't change the existing selection (executing the slot
2814
    //  here and from a recorded macro is treated equally)
2815
0
    if ( pDisp && !bFormulaMode && !rMark.IsMultiMarked() )
2816
0
    {
2817
0
        OUString aAddr;                               // CurrentCell
2818
0
        if( rMark.IsMarked() )
2819
0
        {
2820
0
            const ScRange& aScRange = rMark.GetMarkArea();
2821
0
            aAddr = aScRange.Format(rDoc, ScRefFlags::RANGE_ABS);
2822
0
            if ( aScRange.aStart == aScRange.aEnd )
2823
0
            {
2824
                //  make sure there is a range selection string even for a single cell
2825
0
                aAddr += ":" + aAddr;
2826
0
            }
2827
2828
            //! SID_MARKAREA does not exist anymore ???
2829
            //! What happens when selecting with the cursor ???
2830
0
        }
2831
0
        else                                        // only move cursor
2832
0
        {
2833
0
            ScAddress aScAddress( mrViewData.GetCurX(), mrViewData.GetCurY(), 0 );
2834
0
            aAddr = aScAddress.Format(ScRefFlags::ADDR_ABS);
2835
0
        }
2836
2837
0
        SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
2838
        // We don't want to align to the cursor position because if the
2839
        // cell cursor isn't visible after making selection, it would jump
2840
        // back to the origin of the selection where the cell cursor is.
2841
0
        SfxBoolItem aAlignCursorItem( FN_PARAM_2, false );
2842
0
        pDisp->ExecuteList(SID_CURRENTCELL,
2843
0
                SfxCallMode::SLOT | SfxCallMode::RECORD,
2844
0
                { &aPosItem, &aAlignCursorItem });
2845
2846
0
        mrViewData.GetView()->InvalidateAttribs();
2847
2848
0
    }
2849
0
    mrViewData.GetViewShell()->SelectionChanged();
2850
2851
0
    if (bIsTiledRendering && !bRefMode)
2852
0
    {
2853
0
        OUString aName, aUrl, aTarget;
2854
0
        ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2855
0
        if (pViewShell && nPosX == m_nDownPosX && nPosY == m_nDownPosY
2856
0
            && GetEditUrl(aPos, &aName, &aUrl, &aTarget, &nPosX))
2857
0
        {
2858
0
            OString aMsg(aUrl.toUtf8() + " coordinates: "
2859
0
                         + pViewShell->GetViewData().describeCellCursorAt(nPosX, nPosY) + ", "
2860
0
                         + OString::number(aPos.X() / pViewShell->GetViewData().GetPPTX()) + ", "
2861
0
                         + OString::number(aPos.Y() / pViewShell->GetViewData().GetPPTY()));
2862
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
2863
0
        }
2864
0
    }
2865
2866
0
    m_nDownPosX = m_nDownPosY = -1;
2867
2868
0
    return;
2869
0
}
2870
2871
void ScGridWindow::FakeButtonUp()
2872
0
{
2873
0
    if ( nButtonDown )
2874
0
    {
2875
0
        MouseEvent aEvent( aCurMousePos );      // nButtons = 0 -> ignore
2876
0
        MouseButtonUp( aEvent );
2877
0
    }
2878
0
}
2879
2880
void ScGridWindow::MouseMove( const MouseEvent& rMEvt )
2881
0
{
2882
0
    aCurMousePos = rMEvt.GetPosPixel();
2883
2884
0
    if (rMEvt.IsLeaveWindow() && mpNoteOverlay && !mpNoteOverlay->IsByKeyboard())
2885
0
        HideNoteOverlay();
2886
2887
0
    ScModule* pScMod = ScModule::get();
2888
0
    if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
2889
0
        return;
2890
2891
    // If the Drag&Drop is started in the edit mode then sadly nothing else is kept
2892
0
    if (bEEMouse && nButtonDown && !rMEvt.GetButtons())
2893
0
    {
2894
0
        bEEMouse = false;
2895
0
        nButtonDown = 0;
2896
0
        nMouseStatus = SC_GM_NONE;
2897
0
        return;
2898
0
    }
2899
2900
0
    if (nMouseStatus == SC_GM_IGNORE)
2901
0
        return;
2902
2903
0
    if (nMouseStatus == SC_GM_WATERUNDO)    // Undo in format paintbrush mode -> only what for Up
2904
0
        return;
2905
2906
0
    if ( mrViewData.GetViewShell()->IsAuditShell() )        // Detective Fill Mode
2907
0
    {
2908
0
        SetPointer( PointerStyle::Fill );
2909
0
        return;
2910
0
    }
2911
2912
0
    bool bFormulaMode = pScMod->IsFormulaMode();            // next click -> reference
2913
2914
0
    if (bEEMouse && mrViewData.HasEditView( eWhich ))
2915
0
    {
2916
0
        EditView*   pEditView;
2917
0
        SCCOL       nEditCol;
2918
0
        SCROW       nEditRow;
2919
0
        mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2920
2921
0
        if (comphelper::LibreOfficeKit::isActive() && mrViewData.GetDocument().IsLayoutRTL(mrViewData.CurrentTabForData()))
2922
0
        {
2923
0
            Point aMouse = rMEvt.GetPosPixel();
2924
0
            tools::Rectangle aOutputArea = pEditView->GetOutputArea();
2925
0
            comphelper::ScopeGuard aOutputGuard(
2926
0
                [pEditView, aOutputArea] {
2927
0
                    pEditView->SetOutputArea(aOutputArea);
2928
0
                });
2929
2930
0
            lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
2931
0
            pEditView->SetOutputArea(aOutputArea);
2932
2933
0
            MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
2934
0
                              rMEvt.GetButtons(), rMEvt.GetModifier());
2935
2936
0
            pEditView->MouseMove( aEvent );
2937
0
        }
2938
0
        else
2939
0
            pEditView->MouseMove( rMEvt );
2940
0
        return;
2941
0
    }
2942
2943
0
    if (bDPMouse)
2944
0
    {
2945
0
        DPMouseMove( rMEvt );
2946
0
        return;
2947
0
    }
2948
2949
0
    if (bRFMouse)
2950
0
    {
2951
0
        RFMouseMove( rMEvt, false );
2952
0
        return;
2953
0
    }
2954
2955
0
    if (nPagebreakMouse)
2956
0
    {
2957
0
        PagebreakMove( rMEvt, false );
2958
0
        return;
2959
0
    }
2960
2961
    // Show other mouse pointer?
2962
2963
0
    bool bEditMode = mrViewData.HasEditView(eWhich);
2964
2965
    //! Test if refMode dragging !!!
2966
0
    if ( bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNumber()) )
2967
0
    {
2968
0
        Point   aPos = rMEvt.GetPosPixel();
2969
0
        SCCOL  nPosX;
2970
0
        SCROW  nPosY;
2971
0
        mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2972
2973
0
        EditView*   pEditView;
2974
0
        SCCOL       nEditCol;
2975
0
        SCROW       nEditRow;
2976
0
        mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2977
0
        SCCOL nEndCol = mrViewData.GetEditEndCol();
2978
0
        SCROW nEndRow = mrViewData.GetEditEndRow();
2979
2980
0
        if ( nPosX >= nEditCol && nPosX <= nEndCol &&
2981
0
             nPosY >= nEditRow && nPosY <= nEndRow )
2982
0
        {
2983
0
            if ( !pEditView )
2984
0
            {
2985
0
                SetPointer( PointerStyle::Text );
2986
0
                return;
2987
0
            }
2988
2989
0
            const SvxFieldItem* pFld;
2990
0
            if ( comphelper::LibreOfficeKit::isActive() )
2991
0
            {
2992
0
                Point aLogicClick = pEditView->GetOutputDevice().PixelToLogic(aPos);
2993
0
                pFld = pEditView->GetField( aLogicClick );
2994
0
            }
2995
0
            else
2996
0
            {
2997
0
                pFld = pEditView->GetFieldUnderMousePointer();
2998
0
            }
2999
            //  Field can only be URL field
3000
0
            bool bAlt = rMEvt.IsMod2();
3001
0
            if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() && pFld )
3002
0
                SetPointer( PointerStyle::RefHand );
3003
0
            else if (pEditView->getEditEngine().IsEffectivelyVertical())
3004
0
                SetPointer( PointerStyle::TextVertical );
3005
0
            else
3006
0
                SetPointer( PointerStyle::Text );
3007
0
            return;
3008
0
        }
3009
0
    }
3010
3011
0
    bool bWater = pScMod->GetIsWaterCan() || mrViewData.GetView()->HasPaintBrush();
3012
0
    if (bWater)
3013
0
        SetPointer( PointerStyle::Fill );
3014
3015
0
    if (!bWater)
3016
0
    {
3017
0
        bool bCross = false;
3018
3019
        // range finder
3020
3021
0
        RfCorner rCorner = NONE;
3022
0
        if ( HitRangeFinder( rMEvt.GetPosPixel(), rCorner, nullptr, nullptr, nullptr ) )
3023
0
        {
3024
0
            if (rCorner != NONE)
3025
0
                SetPointer( PointerStyle::Cross );
3026
0
            else
3027
0
                SetPointer( PointerStyle::Hand );
3028
0
            bCross = true;
3029
0
        }
3030
3031
        //  Page-Break-Mode
3032
3033
0
        if ( !nButtonDown && mrViewData.IsPagebreakMode() )
3034
0
        {
3035
0
            sal_uInt16 nBreakType = HitPageBreak( rMEvt.GetPosPixel(), nullptr, nullptr, nullptr );
3036
0
            if (nBreakType != 0 )
3037
0
            {
3038
0
                PointerStyle eNew = PointerStyle::Arrow;
3039
0
                switch ( nBreakType )
3040
0
                {
3041
0
                    case SC_PD_RANGE_L:
3042
0
                    case SC_PD_RANGE_R:
3043
0
                    case SC_PD_BREAK_H:
3044
0
                        eNew = PointerStyle::ESize;
3045
0
                        break;
3046
0
                    case SC_PD_RANGE_T:
3047
0
                    case SC_PD_RANGE_B:
3048
0
                    case SC_PD_BREAK_V:
3049
0
                        eNew = PointerStyle::SSize;
3050
0
                        break;
3051
0
                    case SC_PD_RANGE_TL:
3052
0
                    case SC_PD_RANGE_BR:
3053
0
                        eNew = PointerStyle::SESize;
3054
0
                        break;
3055
0
                    case SC_PD_RANGE_TR:
3056
0
                    case SC_PD_RANGE_BL:
3057
0
                        eNew = PointerStyle::NESize;
3058
0
                        break;
3059
0
                }
3060
0
                SetPointer( eNew );
3061
0
                bCross = true;
3062
0
            }
3063
0
        }
3064
3065
        // Show fill cursor?
3066
3067
0
        if ( !bFormulaMode && !nButtonDown )
3068
0
            if (TestMouse( rMEvt, false ))
3069
0
                bCross = true;
3070
3071
0
        if ( nButtonDown && mrViewData.IsAnyFillMode() )
3072
0
        {
3073
0
            SetPointer( PointerStyle::Cross );
3074
0
            bCross = true;
3075
0
            nScFillModeMouseModifier = rMEvt.GetModifier(); // evaluated for AutoFill and Matrix
3076
0
        }
3077
3078
0
        if (!bCross)
3079
0
        {
3080
0
            bool bAlt = rMEvt.IsMod2();
3081
3082
0
            if (bEditMode)                                  // First has to be in edit mode!
3083
0
                SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
3084
0
            else if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() &&
3085
0
                        GetEditUrl(rMEvt.GetPosPixel()) )
3086
0
                SetPointer( PointerStyle::RefHand );
3087
0
            else if ( DrawMouseMove(rMEvt) )                // Reset pointer
3088
0
                return;
3089
0
        }
3090
0
    }
3091
3092
    // In LOK case, avoid spurious "leavingwindow" mouse move events which has negative coordinates.
3093
    // Such events occur for some reason when a user is selecting a range, (even when not leaving the view area)
3094
    // with one or more other viewers in that sheet.
3095
0
    bool bSkipSelectionUpdate = comphelper::LibreOfficeKit::isActive() &&
3096
0
        rMEvt.IsLeaveWindow() && (aCurMousePos.X() < 0 || aCurMousePos.Y() < 0);
3097
3098
0
    if (!bSkipSelectionUpdate)
3099
0
        mrViewData.GetView()->GetSelEngine()->SelMouseMove( rMEvt );
3100
0
}
3101
3102
static void lcl_InitMouseEvent(css::awt::MouseEvent& rEvent, const MouseEvent& rEvt)
3103
0
{
3104
0
    rEvent.Modifiers = 0;
3105
0
    if ( rEvt.IsShift() )
3106
0
        rEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
3107
0
    if ( rEvt.IsMod1() )
3108
0
        rEvent.Modifiers |= css::awt::KeyModifier::MOD1;
3109
0
    if ( rEvt.IsMod2() )
3110
0
        rEvent.Modifiers |= css::awt::KeyModifier::MOD2;
3111
0
    if ( rEvt.IsMod3() )
3112
0
        rEvent.Modifiers |= css::awt::KeyModifier::MOD3;
3113
3114
0
    rEvent.Buttons = 0;
3115
0
    if ( rEvt.IsLeft() )
3116
0
        rEvent.Buttons |= css::awt::MouseButton::LEFT;
3117
0
    if ( rEvt.IsRight() )
3118
0
        rEvent.Buttons |= css::awt::MouseButton::RIGHT;
3119
0
    if ( rEvt.IsMiddle() )
3120
0
        rEvent.Buttons |= css::awt::MouseButton::MIDDLE;
3121
3122
0
    rEvent.X = rEvt.GetPosPixel().X();
3123
0
    rEvent.Y = rEvt.GetPosPixel().Y();
3124
0
    rEvent.ClickCount = rEvt.GetClicks();
3125
0
    rEvent.PopupTrigger = false;
3126
0
}
3127
3128
bool ScGridWindow::PreNotify( NotifyEvent& rNEvt )
3129
0
{
3130
0
    bool bDone = false;
3131
0
    NotifyEventType nType = rNEvt.GetType();
3132
0
    if ( nType == NotifyEventType::MOUSEBUTTONUP || nType == NotifyEventType::MOUSEBUTTONDOWN )
3133
0
    {
3134
0
        vcl::Window* pWindow = rNEvt.GetWindow();
3135
0
        if (pWindow == this)
3136
0
        {
3137
0
            SfxViewFrame& rViewFrame = mrViewData.GetViewShell()->GetViewFrame();
3138
0
            css::uno::Reference<css::frame::XController> xController = rViewFrame.GetFrame().GetController();
3139
0
            if (xController.is())
3140
0
            {
3141
0
                ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
3142
0
                if (pImp && pImp->IsMouseListening())
3143
0
                {
3144
0
                    css::awt::MouseEvent aEvent;
3145
0
                    lcl_InitMouseEvent( aEvent, *rNEvt.GetMouseEvent() );
3146
0
                    if ( rNEvt.GetWindow() )
3147
0
                        aEvent.Source = rNEvt.GetWindow()->GetComponentInterface();
3148
0
                    if ( nType == NotifyEventType::MOUSEBUTTONDOWN)
3149
0
                        bDone = pImp->MousePressed( aEvent );
3150
0
                    else
3151
0
                        bDone = pImp->MouseReleased( aEvent );
3152
0
                }
3153
0
            }
3154
0
        }
3155
0
    }
3156
0
    if (bDone)      // event consumed by a listener
3157
0
    {
3158
0
        if ( nType == NotifyEventType::MOUSEBUTTONDOWN )
3159
0
        {
3160
0
            const MouseEvent* pMouseEvent = rNEvt.GetMouseEvent();
3161
0
            if ( pMouseEvent->IsRight() && pMouseEvent->GetClicks() == 1 )
3162
0
            {
3163
                // If a listener returned true for a right-click call, also prevent opening the context menu
3164
                // (this works only if the context menu is opened on mouse-down)
3165
0
                nMouseStatus = SC_GM_IGNORE;
3166
0
            }
3167
0
        }
3168
3169
0
        return true;
3170
0
    }
3171
0
    else
3172
0
        return Window::PreNotify( rNEvt );
3173
0
}
3174
3175
void ScGridWindow::Tracking( const TrackingEvent& rTEvt )
3176
0
{
3177
    // Since the SelectionEngine does not track, the events have to be
3178
    // handed to the different MouseHandler...
3179
3180
0
    const MouseEvent& rMEvt = rTEvt.GetMouseEvent();
3181
3182
0
    if ( rTEvt.IsTrackingCanceled() )           // Cancel everything...
3183
0
    {
3184
0
        if (!mrViewData.GetView()->IsInActivatePart() && !ScModule::get()->IsRefDialogOpen())
3185
0
        {
3186
0
            if (bDPMouse)
3187
0
                bDPMouse = false;               // Paint for each bDragRect
3188
0
            if (bDragRect)
3189
0
            {
3190
0
                bDragRect = false;
3191
0
                UpdateDragRectOverlay();
3192
0
            }
3193
0
            if (bRFMouse)
3194
0
            {
3195
0
                RFMouseMove( rMEvt, true );     // Not possible to cancel properly...
3196
0
                bRFMouse = false;
3197
0
            }
3198
0
            if (nPagebreakMouse)
3199
0
            {
3200
0
                bPagebreakDrawn = false;
3201
0
                UpdateDragRectOverlay();
3202
0
                nPagebreakMouse = SC_PD_NONE;
3203
0
            }
3204
3205
0
            SetPointer( PointerStyle::Arrow );
3206
0
            StopMarking();
3207
0
            MouseButtonUp( rMEvt );     // With status SC_GM_IGNORE from StopMarking
3208
3209
0
            bool bRefMode = mrViewData.IsRefMode();
3210
0
            if (bRefMode)
3211
0
                ScModule::get()->EndReference(); // Do not let the Dialog remain minimized
3212
0
        }
3213
0
    }
3214
0
    else if ( rTEvt.IsTrackingEnded() )
3215
0
    {
3216
0
        if (!comphelper::LibreOfficeKit::isActive())
3217
0
        {
3218
            // MouseButtonUp always with matching buttons (eg for test tool, # 63148 #)
3219
            // The tracking event will indicate if it was completed and not canceled.
3220
0
            MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
3221
0
                               rMEvt.GetMode(), nButtonDown, rMEvt.GetModifier() );
3222
0
            MouseButtonUp( aUpEvt );
3223
0
        }
3224
0
    }
3225
0
    else
3226
0
        MouseMove( rMEvt );
3227
0
}
3228
3229
void ScGridWindow::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
3230
0
{
3231
0
    if (mpFilterBox || nPagebreakMouse)
3232
0
        return;
3233
3234
0
    HideNoteOverlay();
3235
3236
0
    CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
3237
3238
0
    if (bEEMouse && mrViewData.HasEditView( eWhich ))
3239
0
    {
3240
0
        EditView*   pEditView;
3241
0
        SCCOL       nEditCol;
3242
0
        SCROW       nEditRow;
3243
0
        mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
3244
3245
        // don't remove the edit view while switching views
3246
0
        ScModule* pScMod = ScModule::get();
3247
0
        pScMod->SetInEditCommand( true );
3248
3249
0
        pEditView->Command( aDragEvent );
3250
3251
0
        ScInputHandler* pHdl = pScMod->GetInputHdl();
3252
0
        if (pHdl)
3253
0
            pHdl->DataChanged();
3254
3255
0
        pScMod->SetInEditCommand( false );
3256
0
        if (!mrViewData.IsActive())             // dropped to different view?
3257
0
        {
3258
0
            ScInputHandler* pViewHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
3259
0
            if ( pViewHdl && mrViewData.HasEditView( eWhich ) )
3260
0
            {
3261
0
                pViewHdl->CancelHandler();
3262
0
                ShowCursor();   // missing from KillEditView
3263
0
            }
3264
0
        }
3265
0
    }
3266
0
    else
3267
0
        if ( !DrawCommand(aDragEvent) )
3268
0
        {
3269
0
            mrViewData.GetView()->GetSelEngine()->Command( aDragEvent );
3270
0
        }
3271
0
}
3272
3273
static void lcl_SetTextCursorPos( ScViewData& rViewData, ScSplitPos eWhich, vcl::Window* pWin )
3274
0
{
3275
0
    SCCOL nCol = rViewData.GetCurX();
3276
0
    SCROW nRow = rViewData.GetCurY();
3277
0
    tools::Rectangle aEditArea = rViewData.GetEditArea( eWhich, nCol, nRow, pWin, nullptr, true );
3278
0
    aEditArea.SetRight( aEditArea.Left() );
3279
0
    aEditArea = pWin->PixelToLogic( aEditArea );
3280
0
    pWin->SetCursorRect( &aEditArea );
3281
0
}
3282
3283
void ScGridWindow::Command( const CommandEvent& rCEvt )
3284
0
{
3285
    // The command event is send to the window after a possible context
3286
    // menu from an inplace client is closed. Now we have the chance to
3287
    // deactivate the inplace client without any problem regarding parent
3288
    // windows and code on the stack.
3289
0
    CommandEventId nCmd = rCEvt.GetCommand();
3290
0
    ScTabViewShell* pTabViewSh = mrViewData.GetViewShell();
3291
0
    SfxInPlaceClient* pClient = pTabViewSh->GetIPClient();
3292
0
    if ( pClient &&
3293
0
         pClient->IsObjectInPlaceActive() &&
3294
0
         nCmd == CommandEventId::ContextMenu )
3295
0
    {
3296
0
        pTabViewSh->DeactivateOle();
3297
0
        return;
3298
0
    }
3299
3300
0
    ScModule* pScMod = ScModule::get();
3301
0
    OSL_ENSURE( nCmd != CommandEventId::StartDrag, "ScGridWindow::Command called with CommandEventId::StartDrag" );
3302
3303
0
    if (nCmd == CommandEventId::ModKeyChange)
3304
0
    {
3305
0
        Window::Command(rCEvt);
3306
0
        return;
3307
0
    }
3308
3309
0
    if ( nCmd == CommandEventId::StartExtTextInput ||
3310
0
         nCmd == CommandEventId::EndExtTextInput ||
3311
0
         nCmd == CommandEventId::ExtTextInput ||
3312
0
         nCmd == CommandEventId::CursorPos ||
3313
0
         nCmd == CommandEventId::QueryCharPosition )
3314
0
    {
3315
0
        bool bEditView = mrViewData.HasEditView( eWhich );
3316
0
        if (!bEditView)
3317
0
        {
3318
            //  only if no cell editview is active, look at drawview
3319
0
            SdrView* pSdrView = mrViewData.GetView()->GetScDrawView();
3320
0
            if ( pSdrView )
3321
0
            {
3322
0
                OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3323
0
                if ( pOlView && pOlView->GetWindow() == this )
3324
0
                {
3325
0
                    pOlView->Command( rCEvt );
3326
0
                    return;                             // done
3327
0
                }
3328
0
            }
3329
0
        }
3330
3331
0
        if ( nCmd == CommandEventId::CursorPos && !bEditView )
3332
0
        {
3333
            //  CURSORPOS may be called without following text input,
3334
            //  to set the input method window position
3335
            //  -> input mode must not be started,
3336
            //  manually calculate text insert position if not in input mode
3337
3338
0
            lcl_SetTextCursorPos( mrViewData, eWhich, this );
3339
0
            return;
3340
0
        }
3341
3342
0
        ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
3343
0
        if ( pHdl )
3344
0
        {
3345
0
            pHdl->InputCommand( rCEvt );
3346
0
            return;                                     // done
3347
0
        }
3348
3349
0
        Window::Command( rCEvt );
3350
0
        return;
3351
0
    }
3352
3353
0
    if ( nCmd == CommandEventId::PasteSelection )
3354
0
    {
3355
0
        if ( bEEMouse )
3356
0
        {
3357
            //  EditEngine handles selection in MouseButtonUp - no action
3358
            //  needed in command handler
3359
0
        }
3360
0
        else
3361
0
        {
3362
0
            PasteSelection( rCEvt.GetMousePosPixel() );
3363
0
        }
3364
0
        return;
3365
0
    }
3366
3367
0
    if ( nCmd == CommandEventId::InputLanguageChange )
3368
0
    {
3369
        // #i55929# Font and font size state depends on input language if nothing is selected,
3370
        // so the slots have to be invalidated when the input language is changed.
3371
3372
0
        SfxBindings& rBindings = mrViewData.GetBindings();
3373
0
        rBindings.Invalidate( SID_ATTR_CHAR_FONT );
3374
0
        rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
3375
0
        return;
3376
0
    }
3377
3378
0
    if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
3379
0
    {
3380
0
        bool bDone = mrViewData.GetView()->ScrollCommand( rCEvt, eWhich );
3381
0
        if (!bDone)
3382
0
            Window::Command(rCEvt);
3383
0
        return;
3384
0
    }
3385
3386
0
    if (nCmd == CommandEventId::GesturePan)
3387
0
    {
3388
0
        bool bDone = mrViewData.GetView()->GesturePanCommand(rCEvt);
3389
0
        if (!bDone)
3390
0
            Window::Command(rCEvt);
3391
0
        return;
3392
0
    }
3393
3394
0
    if (nCmd == CommandEventId::GestureZoom)
3395
0
    {
3396
0
        bool bDone = mrViewData.GetView()->GestureZoomCommand(rCEvt);
3397
0
        if (!bDone)
3398
0
            Window::Command(rCEvt);
3399
0
        return;
3400
0
    }
3401
3402
    // #i7560# FormulaMode check is below scrolling - scrolling is allowed during formula input
3403
0
    bool bDisable = pScMod->IsFormulaMode() ||
3404
0
                    pScMod->IsModalMode(mrViewData.GetSfxDocShell());
3405
0
    if (bDisable)
3406
0
        return;
3407
3408
0
    if (nCmd != CommandEventId::ContextMenu || pScMod->GetIsWaterCan())
3409
0
        return;
3410
3411
0
    bool bMouse = rCEvt.IsMouseEvent();
3412
0
    if ( bMouse && nMouseStatus == SC_GM_IGNORE )
3413
0
        return;
3414
3415
0
    if (mrViewData.IsAnyFillMode())
3416
0
    {
3417
0
        mrViewData.GetView()->StopRefMode();
3418
0
        mrViewData.ResetFillMode();
3419
0
    }
3420
0
    ReleaseMouse();
3421
0
    StopMarking();
3422
3423
0
    Point aPosPixel = rCEvt.GetMousePosPixel();
3424
0
    Point aMenuPos = aPosPixel;
3425
3426
0
    bool bPosIsInEditView = mrViewData.HasEditView(eWhich);
3427
0
    SCCOL nCellX = -1;
3428
0
    SCROW nCellY = -1;
3429
0
    mrViewData.GetPosFromPixel(aPosPixel.X(), aPosPixel.Y(), eWhich, nCellX, nCellY);
3430
    // GetPosFromPixel ignores the fact that when editing a cell, the cell might grow to cover
3431
    // other rows/columns. In addition, the mouse might now be outside the edited cell.
3432
0
    if (bPosIsInEditView)
3433
0
    {
3434
0
        if (nCellX >= mrViewData.GetEditViewCol() && nCellX <= mrViewData.GetEditEndCol())
3435
0
            nCellX = mrViewData.GetEditViewCol();
3436
0
        else
3437
0
            bPosIsInEditView = false;
3438
3439
0
        if (nCellY >= mrViewData.GetEditViewRow() && nCellY <= mrViewData.GetEditEndRow())
3440
0
            nCellY = mrViewData.GetEditViewRow();
3441
0
        else
3442
0
            bPosIsInEditView = false;
3443
3444
0
        if (!bPosIsInEditView)
3445
0
        {
3446
            // Close the edit view when moving outside of the edited cell
3447
            // to avoid showing the edit popup, or providing the wrong EditView to spellcheck.
3448
0
            ScInputHandler* pHdl = pScMod->GetInputHdl();
3449
0
            if (pHdl)
3450
0
                pHdl->EnterHandler();
3451
0
        }
3452
0
    }
3453
3454
0
    bool bSpellError = false;
3455
0
    SCCOL nColSpellError = nCellX;
3456
3457
0
    if ( bMouse )
3458
0
    {
3459
0
        ScDocument& rDoc = mrViewData.GetDocument();
3460
0
        SCTAB nTab = mrViewData.CurrentTabForData();
3461
0
        const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
3462
0
        bool bSelectAllowed = true;
3463
0
        if ( pProtect && pProtect->isProtected() )
3464
0
        {
3465
            // This sheet is protected.  Check if a context menu is allowed on this cell.
3466
0
            bool bCellProtected = rDoc.HasAttrib(nCellX, nCellY, nTab, nCellX, nCellY, nTab, HasAttrFlags::Protected);
3467
0
            bool bSelProtected   = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
3468
0
            bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
3469
3470
0
            if (bCellProtected)
3471
0
                bSelectAllowed = bSelProtected;
3472
0
            else
3473
0
                bSelectAllowed = bSelUnprotected;
3474
0
        }
3475
0
        if (!bSelectAllowed)
3476
            // Selecting this cell is not allowed, neither is context menu.
3477
0
            return;
3478
3479
0
        if (mpSpellCheckCxt
3480
0
            && (bPosIsInEditView || !mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY)))
3481
0
        {
3482
            // Find the first string to the left for spell checking in case the current cell is empty.
3483
0
            ScAddress aPos(nCellX, nCellY, nTab);
3484
0
            ScRefCellValue aSpellCheckCell(rDoc, aPos);
3485
0
            while (!bPosIsInEditView && aSpellCheckCell.getType() == CELLTYPE_NONE)
3486
0
            {
3487
                // Loop until we get the first non-empty cell in the row.
3488
0
                aPos.IncCol(-1);
3489
0
                if (aPos.Col() < 0)
3490
0
                    break;
3491
3492
0
                aSpellCheckCell.assign(rDoc, aPos);
3493
0
            }
3494
3495
0
            if (aPos.Col() >= 0 && (aSpellCheckCell.getType() == CELLTYPE_STRING || aSpellCheckCell.getType() == CELLTYPE_EDIT))
3496
0
                nColSpellError = aPos.Col();
3497
3498
            // Is there possibly a misspelled word somewhere in the cell?
3499
            // A "yes" does not mean that the word under the mouse pointer is wrong though.
3500
0
            bSpellError = (mpSpellCheckCxt->isMisspelled(nColSpellError, nCellY));
3501
            // Avoid situations where selecting the cell-with-wrong-spelling would be bad
3502
0
            if (bSpellError)
3503
0
            {
3504
                // When the mouse is over an empty cell, text with spelling errors
3505
                // potentially could have overflowed underneath the mouse pointer
3506
0
                if (nColSpellError != nCellX)
3507
0
                {
3508
                    // If the mouse is over a selected cell, only consider spell-checking
3509
                    // if the cell with the misspelling is also selected. tdf#157038
3510
0
                    if (mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY))
3511
0
                        bSpellError = mrViewData.GetMarkData().IsCellMarked(nColSpellError, nCellY);
3512
0
                }
3513
0
            }
3514
0
        }
3515
3516
        //  #i18735# First select the item under the mouse pointer.
3517
        //  This can change the selection, and the view state (edit mode, etc).
3518
0
        SelectForContextMenu(aPosPixel, bSpellError ? nColSpellError : nCellX, nCellY);
3519
0
    }
3520
3521
0
    bool bDone = false;
3522
0
    bool bEdit = mrViewData.HasEditView(eWhich);
3523
3524
0
    if ( !bEdit )
3525
0
    {
3526
        // Edit cell with spelling errors?
3527
        // tdf#127341 the formerly used GetEditUrl(aPosPixel) additionally
3528
        // to bSpellError activated EditMode here for right-click on URL
3529
        // which prevents the regular context-menu from appearing. Since this
3530
        // is more expected than the context-menu for editing a URL, I removed
3531
        // this. If this was wanted and can be argued it might be re-activated.
3532
        // For now, reduce to spelling errors - as the original comment above
3533
        // suggests.
3534
0
        if (bMouse && bSpellError)
3535
0
        {
3536
            //  GetEditUrlOrError has already moved the Cursor
3537
3538
0
            pScMod->SetInputMode( SC_INPUT_TABLE );
3539
0
            bEdit = mrViewData.HasEditView(eWhich);     // Did it work?
3540
3541
0
            OSL_ENSURE( bEdit, "Can not be switched in edit mode" );
3542
0
        }
3543
0
    }
3544
0
    if ( bEdit )
3545
0
    {
3546
0
        EditView* pEditView = mrViewData.GetEditView( eWhich );     // is then not 0
3547
3548
0
        if ( !bMouse )
3549
0
        {
3550
0
            vcl::Cursor* pCur = pEditView->GetCursor();
3551
0
            if ( pCur )
3552
0
            {
3553
0
                Point aLogicPos = pCur->GetPos();
3554
                //  use the position right of the cursor (spell popup is opened if
3555
                //  the cursor is before the word, but not if behind it)
3556
0
                aLogicPos.AdjustX(pCur->GetWidth() );
3557
0
                aLogicPos.AdjustY(pCur->GetHeight() / 2 );     // center vertically
3558
0
                aMenuPos = LogicToPixel( aLogicPos );
3559
0
            }
3560
0
        }
3561
3562
        //  if edit mode was just started above, online spelling may be incomplete
3563
0
        pEditView->getEditEngine().CompleteOnlineSpelling();
3564
3565
        //  IsCursorAtWrongSpelledWord could be used for !bMouse
3566
        //  if there was a corresponding ExecuteSpellPopup call
3567
3568
0
        if (bSpellError)
3569
0
        {
3570
            // On OS/2 when clicking next to the Popup menu, the MouseButtonDown
3571
            // comes before the end of menu execute, thus the SetModified has to
3572
            // be done prior to this (Bug #40968#)
3573
0
            ScInputHandler* pHdl = pScMod->GetInputHdl();
3574
0
            if (pHdl)
3575
0
                pHdl->SetModified();
3576
3577
0
            const OUString sOldText = pHdl ? pHdl->GetEditString() : u""_ustr;
3578
3579
            // Only done/shown if a misspelled word is actually under the mouse pointer.
3580
0
            Link<SpellCallbackInfo&,void> aLink = LINK( this, ScGridWindow, PopupSpellingHdl );
3581
0
            bDone = pEditView->ExecuteSpellPopup(aMenuPos, aLink);
3582
3583
            // If the spelling is corrected, stop editing to flush any cached spelling info.
3584
            // Or, if no misspelled word at this position, and it wasn't initially in edit mode,
3585
            // then exit the edit mode in order to get the full context popup (not edit popup).
3586
0
            if (pHdl && (pHdl->GetEditString() != sOldText
3587
0
                         || (!bDone && !bPosIsInEditView)))
3588
0
            {
3589
0
                pHdl->EnterHandler();
3590
0
            }
3591
3592
0
            if (!bDone && nColSpellError != nCellX)
3593
0
            {
3594
                // NOTE: This call can change the selection, and the view state (edit mode, etc).
3595
0
                SelectForContextMenu(aPosPixel, nCellX, nCellY);
3596
0
            }
3597
0
        }
3598
0
    }
3599
0
    else if ( !bMouse )
3600
0
    {
3601
        //  non-edit menu by keyboard -> use lower right of cell cursor position
3602
0
        ScDocument& rDoc = mrViewData.GetDocument();
3603
0
        SCTAB nTabNo = mrViewData.CurrentTabForData();
3604
0
        bool bLayoutIsRTL = rDoc.IsLayoutRTL(nTabNo);
3605
3606
0
        SCCOL nCurX = mrViewData.GetCurX();
3607
0
        SCROW nCurY = mrViewData.GetCurY();
3608
0
        aMenuPos = mrViewData.GetScrPos( nCurX, nCurY, eWhich, true );
3609
0
        tools::Long nSizeXPix;
3610
0
        tools::Long nSizeYPix;
3611
0
        mrViewData.GetMergeSizePixel( nCurX, nCurY, nSizeXPix, nSizeYPix );
3612
        // fdo#55432 take the correct position for RTL sheet
3613
0
        aMenuPos.AdjustX(bLayoutIsRTL ? -nSizeXPix : nSizeXPix );
3614
0
        aMenuPos.AdjustY(nSizeYPix );
3615
3616
0
        ScTabViewShell* pViewSh = mrViewData.GetViewShell();
3617
0
        if (pViewSh)
3618
0
        {
3619
            //  Is a draw object selected?
3620
3621
0
            SdrView* pDrawView = pViewSh->GetScDrawView();
3622
0
            if (pDrawView && pDrawView->GetMarkedObjectList().GetMarkCount() != 0)
3623
0
            {
3624
                // #100442#; the context menu should open in the middle of the selected objects
3625
0
                tools::Rectangle aSelectRect(LogicToPixel(pDrawView->GetAllMarkedBoundRect()));
3626
0
                aMenuPos = aSelectRect.Center();
3627
0
            }
3628
0
        }
3629
0
    }
3630
3631
0
    if (bDone)
3632
0
        return;
3633
3634
0
    SfxDispatcher::ExecutePopup( this, &aMenuPos );
3635
0
}
3636
3637
void ScGridWindow::SelectForContextMenu( const Point& rPosPixel, SCCOL nCellX, SCROW nCellY )
3638
0
{
3639
    //  #i18735# if the click was outside of the current selection,
3640
    //  the cursor is moved or an object at the click position selected.
3641
    //  (see SwEditWin::SelectMenuPosition in Writer)
3642
3643
0
    ScTabView* pView = mrViewData.GetView();
3644
0
    ScDrawView* pDrawView = pView->GetScDrawView();
3645
3646
    //  check cell edit mode
3647
3648
0
    if ( mrViewData.HasEditView(eWhich) )
3649
0
    {
3650
0
        ScModule* pScMod = ScModule::get();
3651
0
        SCCOL nEditStartCol = mrViewData.GetEditViewCol(); //! change to GetEditStartCol after calcrtl is integrated
3652
0
        SCROW nEditStartRow = mrViewData.GetEditViewRow();
3653
0
        SCCOL nEditEndCol = mrViewData.GetEditEndCol();
3654
0
        SCROW nEditEndRow = mrViewData.GetEditEndRow();
3655
3656
0
        if ( nCellX >= nEditStartCol && nCellX <= nEditEndCol &&
3657
0
             nCellY >= nEditStartRow && nCellY <= nEditEndRow )
3658
0
        {
3659
            //  handle selection within the EditView
3660
3661
0
            EditView* pEditView = mrViewData.GetEditView( eWhich );     // not NULL (HasEditView)
3662
0
            EditEngine& rEditEngine = pEditView->getEditEngine();
3663
0
            tools::Rectangle aOutputArea = pEditView->GetOutputArea();
3664
0
            tools::Rectangle aVisArea = pEditView->GetVisArea();
3665
3666
0
            Point aTextPos = PixelToLogic( rPosPixel );
3667
0
            if (rEditEngine.IsEffectivelyVertical())            // have to manually transform position
3668
0
            {
3669
0
                aTextPos -= aOutputArea.TopRight();
3670
0
                tools::Long nTemp = -aTextPos.X();
3671
0
                aTextPos.setX( aTextPos.Y() );
3672
0
                aTextPos.setY( nTemp );
3673
0
            }
3674
0
            else
3675
0
                aTextPos -= aOutputArea.TopLeft();
3676
0
            aTextPos += aVisArea.TopLeft();             // position in the edit document
3677
3678
0
            ESelection aCompare(rEditEngine.FindDocPosition(aTextPos));
3679
0
            ESelection aSelection = pEditView->GetSelection();
3680
0
            aSelection.Adjust();    // needed for IsLess/IsGreater
3681
0
            if ( aCompare < aSelection || aCompare > aSelection )
3682
0
            {
3683
                // clicked outside the selected text - deselect and move text cursor
3684
0
                MouseEvent aEvent( rPosPixel );
3685
0
                pEditView->MouseButtonDown( aEvent );
3686
0
                pEditView->MouseButtonUp( aEvent );
3687
0
                pScMod->InputSelection( pEditView );
3688
0
            }
3689
3690
0
            return;     // clicked within the edit view - keep edit mode
3691
0
        }
3692
0
        else
3693
0
        {
3694
            // outside of the edit view - end edit mode, regardless of cell selection, then continue
3695
0
            pScMod->InputEnterHandler();
3696
0
        }
3697
0
    }
3698
3699
    //  check draw text edit mode
3700
3701
0
    Point aLogicPos = PixelToLogic( rPosPixel );        // after cell edit mode is ended
3702
0
    if ( pDrawView && pDrawView->GetTextEditObject() && pDrawView->GetTextEditOutlinerView() )
3703
0
    {
3704
0
        OutlinerView* pOlView = pDrawView->GetTextEditOutlinerView();
3705
0
        tools::Rectangle aOutputArea = pOlView->GetOutputArea();
3706
0
        if ( aOutputArea.Contains( aLogicPos ) )
3707
0
        {
3708
            //  handle selection within the OutlinerView
3709
3710
0
            Outliner& rOutliner = pOlView->GetOutliner();
3711
0
            const EditEngine& rEditEngine = rOutliner.GetEditEngine();
3712
0
            tools::Rectangle aVisArea = pOlView->GetVisArea();
3713
3714
0
            Point aTextPos = aLogicPos;
3715
0
            if ( rOutliner.IsVertical() )              // have to manually transform position
3716
0
            {
3717
0
                aTextPos -= aOutputArea.TopRight();
3718
0
                tools::Long nTemp = -aTextPos.X();
3719
0
                aTextPos.setX( aTextPos.Y() );
3720
0
                aTextPos.setY( nTemp );
3721
0
            }
3722
0
            else
3723
0
                aTextPos -= aOutputArea.TopLeft();
3724
0
            aTextPos += aVisArea.TopLeft();             // position in the edit document
3725
3726
0
            ESelection aCompare(rEditEngine.FindDocPosition(aTextPos));
3727
0
            ESelection aSelection = pOlView->GetSelection();
3728
0
            aSelection.Adjust();    // needed for IsLess/IsGreater
3729
0
            if ( aCompare < aSelection || aCompare > aSelection )
3730
0
            {
3731
                // clicked outside the selected text - deselect and move text cursor
3732
                // use DrawView to allow extra handling there (none currently)
3733
0
                MouseEvent aEvent( rPosPixel );
3734
0
                pDrawView->MouseButtonDown( aEvent, GetOutDev() );
3735
0
                pDrawView->MouseButtonUp( aEvent, GetOutDev() );
3736
0
            }
3737
3738
0
            return;     // clicked within the edit area - keep edit mode
3739
0
        }
3740
0
        else
3741
0
        {
3742
            // Outside of the edit area - end text edit mode, then continue.
3743
            // DrawDeselectAll also ends text edit mode and updates the shells.
3744
            // If the click was on the edited object, it will be selected again below.
3745
0
            pView->DrawDeselectAll();
3746
0
        }
3747
0
    }
3748
3749
    //  look for existing selection
3750
3751
0
    bool bHitSelected = false;
3752
0
    if ( pDrawView && pDrawView->IsMarkedObjHit( aLogicPos ) )
3753
0
    {
3754
        //  clicked on selected object -> don't change anything
3755
0
        bHitSelected = true;
3756
0
    }
3757
0
    else if ( mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY) )
3758
0
    {
3759
        //  clicked on selected cell -> don't change anything
3760
0
        bHitSelected = true;
3761
0
    }
3762
3763
    //  select drawing object or move cell cursor
3764
3765
0
    if ( bHitSelected )
3766
0
        return;
3767
3768
0
    bool bWasDraw = ( pDrawView && pDrawView->GetMarkedObjectList().GetMarkCount() != 0 );
3769
0
    bool bHitDraw = false;
3770
0
    if ( pDrawView )
3771
0
    {
3772
0
        pDrawView->UnmarkAllObj();
3773
        // Unlock the Internal Layer in order to activate the context menu.
3774
        // re-lock in ScDrawView::MarkListHasChanged()
3775
0
        lcl_UnLockComment(pDrawView, aLogicPos, mrViewData);
3776
0
        bHitDraw = pDrawView->MarkObj( aLogicPos );
3777
        // draw shell is activated in MarkListHasChanged
3778
0
    }
3779
0
    if ( !bHitDraw )
3780
0
    {
3781
0
        pView->Unmark();
3782
0
        pView->SetCursor(nCellX, nCellY);
3783
0
        if ( bWasDraw )
3784
0
            mrViewData.GetViewShell()->SetDrawShell( false );   // switch shells
3785
0
    }
3786
0
}
3787
3788
void ScGridWindow::KeyInput(const KeyEvent& rKEvt)
3789
0
{
3790
    // Cursor control for ref input dialog
3791
0
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
3792
3793
#ifdef DBG_UTIL
3794
3795
    if (rKeyCode.IsMod1() && rKeyCode.IsShift())
3796
    {
3797
        if (rKeyCode.GetCode() == KEY_F12)
3798
        {
3799
            dumpColumnInformationPixel();
3800
        }
3801
        else if (rKeyCode.GetCode() == KEY_F11)
3802
        {
3803
            dumpGraphicInformation();
3804
        }
3805
        else if (rKeyCode.GetCode() == KEY_F10)
3806
        {
3807
            dumpColumnInformationHmm();
3808
        }
3809
        else if (rKeyCode.GetCode() == KEY_F6)
3810
        {
3811
            dumpCellProperties();
3812
        }
3813
        else if (rKeyCode.GetCode() == KEY_F8)
3814
        {
3815
            dumpColumnCellStorage();
3816
        }
3817
        else if (rKeyCode.GetCode() == KEY_F7)
3818
        {
3819
            ScDocument& rDoc = mrViewData.GetDocument();
3820
            auto& rMapper = rDoc.GetExternalDataMapper();
3821
            for (auto& itr : rMapper.getDataSources())
3822
            {
3823
                itr.refresh(&rDoc);
3824
            }
3825
            return;
3826
        }
3827
        else if (rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_F5)
3828
        {
3829
            dumpPivotTable();
3830
        }
3831
    }
3832
3833
#endif
3834
3835
0
    ScModule* mod = ScModule::get();
3836
0
    if( mod->IsRefDialogOpen() )
3837
0
    {
3838
0
        if( !rKeyCode.GetModifier() && (rKeyCode.GetCode() == KEY_F2) )
3839
0
        {
3840
0
            mod->EndReference();
3841
0
        }
3842
0
        else if( mrViewData.GetViewShell()->MoveCursorKeyInput( rKEvt ) )
3843
0
        {
3844
0
            ScRange aRef(
3845
0
                mrViewData.GetRefStartX(), mrViewData.GetRefStartY(), mrViewData.GetRefStartZ(),
3846
0
                mrViewData.GetRefEndX(), mrViewData.GetRefEndY(), mrViewData.GetRefEndZ() );
3847
0
            mod->SetReference(aRef, mrViewData.GetDocument());
3848
0
        }
3849
0
        mrViewData.GetViewShell()->SelectionChanged();
3850
0
        return ;
3851
0
    }
3852
0
    else if( rKeyCode.GetCode() == KEY_RETURN && mrViewData.IsPasteMode()
3853
0
            && mod->GetInputOptions().GetEnterPasteMode() )
3854
0
    {
3855
0
        ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
3856
0
        ScClipUtil::PasteFromClipboard( mrViewData, pTabViewShell, true );
3857
3858
        // Clear clipboard content.
3859
0
        uno::Reference<datatransfer::clipboard::XClipboard> xSystemClipboard =
3860
0
            GetClipboard();
3861
0
        if (xSystemClipboard.is())
3862
0
        {
3863
0
            xSystemClipboard->setContents(
3864
0
                    uno::Reference<datatransfer::XTransferable>(),
3865
0
                    uno::Reference<datatransfer::clipboard::XClipboardOwner>());
3866
0
        }
3867
3868
        // hide the border around the copy source
3869
0
        mrViewData.SetPasteMode( ScPasteFlags::NONE );
3870
        // Clear CopySourceOverlay in each window of a split/frozen tabview
3871
0
        mrViewData.GetView()->UpdateCopySourceOverlay();
3872
0
        return;
3873
0
    }
3874
    // if semi-modeless SfxChildWindow dialog above, then no KeyInputs:
3875
0
    else if( !mrViewData.IsAnyFillMode() )
3876
0
    {
3877
0
        if (rKeyCode.GetCode() == KEY_ESCAPE)
3878
0
        {
3879
0
            mrViewData.SetPasteMode( ScPasteFlags::NONE );
3880
            // Clear CopySourceOverlay in each window of a split/frozen tabview
3881
0
            mrViewData.GetView()->UpdateCopySourceOverlay();
3882
0
        }
3883
        //  query for existing note marker before calling ViewShell's keyboard handling
3884
        //  which may remove the marker
3885
0
        bool bHadKeyMarker(mpNoteOverlay && mpNoteOverlay->IsByKeyboard());
3886
0
        ScTabViewShell* pViewSh = mrViewData.GetViewShell();
3887
3888
0
        if (mrViewData.GetDocShell()->GetProgress())
3889
0
            return;
3890
3891
0
        if (DrawKeyInput(rKEvt, this))
3892
0
        {
3893
0
            const vcl::KeyCode& rLclKeyCode = rKEvt.GetKeyCode();
3894
0
            if (rLclKeyCode.GetCode() == KEY_DOWN
3895
0
                || rLclKeyCode.GetCode() == KEY_UP
3896
0
                || rLclKeyCode.GetCode() == KEY_LEFT
3897
0
                || rLclKeyCode.GetCode() == KEY_RIGHT)
3898
0
            {
3899
0
                ScTabViewShell* pViewShell = mrViewData.GetViewShell();
3900
0
                SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
3901
0
                rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
3902
0
                rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
3903
0
            }
3904
0
            return;
3905
0
        }
3906
3907
0
        if (!mrViewData.GetView()->IsDrawSelMode() && !DrawHasMarkedObj())  // No entries in draw mode
3908
0
        {                                                           //! check DrawShell !!!
3909
0
            if (pViewSh->TabKeyInput(rKEvt))
3910
0
                return;
3911
0
        }
3912
0
        else
3913
0
            if (pViewSh->SfxViewShell::KeyInput(rKEvt))             // from SfxViewShell
3914
0
                return;
3915
3916
0
        vcl::KeyCode aCode = rKEvt.GetKeyCode();
3917
0
        if ( aCode.GetCode() == KEY_ESCAPE && aCode.GetModifier() == 0 )
3918
0
        {
3919
0
            if ( bHadKeyMarker )
3920
0
                HideNoteOverlay();
3921
0
            else
3922
0
                pViewSh->Escape();
3923
0
            return;
3924
0
        }
3925
0
        if ( aCode.GetCode() == KEY_F1 && aCode.GetModifier() == KEY_MOD1 )
3926
0
        {
3927
            //  ctrl-F1 shows or hides the note or redlining info for the cursor position
3928
            //  (hard-coded because F1 can't be configured)
3929
3930
0
            if ( bHadKeyMarker )
3931
0
                HideNoteOverlay();       // hide when previously visible
3932
0
            else
3933
0
                ShowNoteMarker( mrViewData.GetCurX(), mrViewData.GetCurY(), true );
3934
0
            return;
3935
0
        }
3936
0
        if (aCode.GetCode() == KEY_BRACKETLEFT && aCode.GetModifier() == KEY_MOD1)
3937
0
        {
3938
0
            pViewSh->DetectiveMarkPred();
3939
0
            return;
3940
0
        }
3941
0
        if (aCode.GetCode() == KEY_BRACKETRIGHT && aCode.GetModifier() == KEY_MOD1)
3942
0
        {
3943
0
            pViewSh->DetectiveMarkSucc();
3944
0
            return;
3945
0
        }
3946
3947
0
    }
3948
3949
0
    Window::KeyInput(rKEvt);
3950
0
}
3951
3952
OUString ScGridWindow::GetSurroundingText() const
3953
0
{
3954
0
    bool bEditView = mrViewData.HasEditView(eWhich);
3955
0
    if (bEditView)
3956
0
    {
3957
0
        ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
3958
0
        if (pHdl)
3959
0
            return pHdl->GetSurroundingText();
3960
0
    }
3961
0
    else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3962
0
    {
3963
        // if no cell editview is active, look at drawview
3964
0
        OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3965
0
        if (pOlView && pOlView->GetWindow() == this)
3966
0
            return pOlView->GetSurroundingText();
3967
0
    }
3968
3969
0
    return Window::GetSurroundingText();
3970
0
}
3971
3972
Selection ScGridWindow::GetSurroundingTextSelection() const
3973
0
{
3974
0
    bool bEditView = mrViewData.HasEditView(eWhich);
3975
0
    if (bEditView)
3976
0
    {
3977
0
        ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
3978
0
        if (pHdl)
3979
0
            return pHdl->GetSurroundingTextSelection();
3980
0
    }
3981
0
    else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3982
0
    {
3983
        // if no cell editview is active, look at drawview
3984
0
        OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3985
0
        if (pOlView && pOlView->GetWindow() == this)
3986
0
            return pOlView->GetSurroundingTextSelection();
3987
0
    }
3988
3989
0
    return Window::GetSurroundingTextSelection();
3990
0
}
3991
3992
bool ScGridWindow::DeleteSurroundingText(const Selection& rSelection)
3993
0
{
3994
0
    bool bEditView = mrViewData.HasEditView(eWhich);
3995
0
    if (bEditView)
3996
0
    {
3997
0
        ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
3998
0
        if (pHdl)
3999
0
            return pHdl->DeleteSurroundingText(rSelection);
4000
0
    }
4001
0
    else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
4002
0
    {
4003
        // if no cell editview is active, look at drawview
4004
0
        OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
4005
0
        if (pOlView && pOlView->GetWindow() == this)
4006
0
            return pOlView->DeleteSurroundingText(rSelection);
4007
0
    }
4008
4009
0
    return Window::DeleteSurroundingText(rSelection);
4010
0
}
4011
4012
void ScGridWindow::StopMarking()
4013
0
{
4014
0
    DrawEndAction();                // Cancel Select/move on Drawing-Layer
4015
4016
0
    if (nButtonDown)
4017
0
    {
4018
0
        mrViewData.GetMarkData().SetMarking(false);
4019
0
        nMouseStatus = SC_GM_IGNORE;
4020
0
    }
4021
0
}
4022
4023
void ScGridWindow::UpdateInputContext()
4024
0
{
4025
0
    bool bReadOnly = mrViewData.GetDocShell()->IsReadOnly();
4026
0
    InputContextFlags nOptions = bReadOnly ? InputContextFlags::NONE : ( InputContextFlags::Text | InputContextFlags::ExtText );
4027
4028
    //  when font from InputContext is used,
4029
    //  it must be taken from the cursor position's cell attributes
4030
4031
0
    InputContext aContext;
4032
0
    aContext.SetOptions( nOptions );
4033
0
    SetInputContext( aContext );
4034
0
}
4035
4036
                                // sensitive range (Pixel)
4037
0
#define SCROLL_SENSITIVE 20
4038
4039
void ScGridWindow::DropScroll( const Point& rMousePos )
4040
0
{
4041
0
    ScDocument& rDoc = mrViewData.GetDocument();
4042
0
    SCCOL nDx = 0;
4043
0
    SCROW nDy = 0;
4044
0
    Size aSize = GetOutputSizePixel();
4045
4046
0
    if (aSize.Width() > SCROLL_SENSITIVE * 3)
4047
0
    {
4048
0
        if ( rMousePos.X() < SCROLL_SENSITIVE && mrViewData.GetPosX(WhichH(eWhich)) > 0 )
4049
0
            nDx = -1;
4050
0
        if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE
4051
0
                && mrViewData.GetPosX(WhichH(eWhich)) < rDoc.MaxCol() )
4052
0
            nDx = 1;
4053
0
    }
4054
0
    if (aSize.Height() > SCROLL_SENSITIVE * 3)
4055
0
    {
4056
0
        if ( rMousePos.Y() < SCROLL_SENSITIVE && mrViewData.GetPosY(WhichV(eWhich)) > 0 )
4057
0
            nDy = -1;
4058
0
        if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE
4059
0
                && mrViewData.GetPosY(WhichV(eWhich)) < rDoc.MaxRow() )
4060
0
            nDy = 1;
4061
0
    }
4062
4063
0
    if ( nDx != 0 || nDy != 0 )
4064
0
    {
4065
0
        if ( nDx != 0 )
4066
0
            mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
4067
0
        if ( nDy != 0 )
4068
0
            mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
4069
0
    }
4070
0
}
4071
4072
static bool lcl_TestScenarioRedliningDrop( const ScDocument* pDoc, const ScRange& aDragRange)
4073
0
{
4074
    //  Test, if a scenario is affected by a drop when turning on RedLining,
4075
0
    bool bReturn = false;
4076
0
    SCTAB nTab = aDragRange.aStart.Tab();
4077
0
    SCTAB nTabCount = pDoc->GetTableCount();
4078
4079
0
    if(pDoc->GetChangeTrack()!=nullptr)
4080
0
    {
4081
0
        if( pDoc->IsScenario(nTab) && pDoc->HasScenarioRange(nTab, aDragRange))
4082
0
        {
4083
0
            bReturn = true;
4084
0
        }
4085
0
        else
4086
0
        {
4087
0
            for(SCTAB i=nTab+1; i<nTabCount && pDoc->IsScenario(i); i++)
4088
0
            {
4089
0
                if(pDoc->HasScenarioRange(i, aDragRange))
4090
0
                {
4091
0
                    bReturn = true;
4092
0
                    break;
4093
0
                }
4094
0
            }
4095
0
        }
4096
0
    }
4097
0
    return bReturn;
4098
0
}
4099
4100
static ScRange lcl_MakeDropRange( const ScDocument& rDoc, SCCOL nPosX, SCROW nPosY, SCTAB nTab, const ScRange& rSource )
4101
0
{
4102
0
    SCCOL nCol1 = nPosX;
4103
0
    SCCOL nCol2 = nCol1 + ( rSource.aEnd.Col() - rSource.aStart.Col() );
4104
0
    if ( nCol2 > rDoc.MaxCol() )
4105
0
    {
4106
0
        nCol1 -= nCol2 - rDoc.MaxCol();
4107
0
        nCol2 = rDoc.MaxCol();
4108
0
    }
4109
0
    SCROW nRow1 = nPosY;
4110
0
    SCROW nRow2 = nRow1 + ( rSource.aEnd.Row() - rSource.aStart.Row() );
4111
0
    if ( nRow2 > rDoc.MaxRow() )
4112
0
    {
4113
0
        nRow1 -= nRow2 - rDoc.MaxRow();
4114
0
        nRow2 = rDoc.MaxRow();
4115
0
    }
4116
4117
0
    return ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
4118
0
}
4119
4120
sal_Int8 ScGridWindow::AcceptPrivateDrop( const AcceptDropEvent& rEvt, const ScDragData& rData )
4121
0
{
4122
0
    if ( rEvt.mbLeaving )
4123
0
    {
4124
0
        bDragRect = false;
4125
0
        UpdateDragRectOverlay();
4126
0
        return rEvt.mnAction;
4127
0
    }
4128
4129
0
    if ( rData.pCellTransfer )
4130
0
    {
4131
        // Don't move source that would include filtered rows.
4132
0
        if ((rEvt.mnAction & DND_ACTION_MOVE) && rData.pCellTransfer->HasFilteredRows())
4133
0
        {
4134
0
            if (bDragRect)
4135
0
            {
4136
0
                bDragRect = false;
4137
0
                UpdateDragRectOverlay();
4138
0
            }
4139
0
            return DND_ACTION_NONE;
4140
0
        }
4141
4142
0
        Point aPos = rEvt.maPosPixel;
4143
4144
0
        ScDocument* pSourceDoc = rData.pCellTransfer->GetSourceDocument();
4145
0
        ScDocument& rThisDoc   = mrViewData.GetDocument();
4146
0
        if (pSourceDoc == &rThisDoc)
4147
0
        {
4148
0
            OUString aName;
4149
0
            if ( rThisDoc.HasChartAtPoint(mrViewData.CurrentTabForData(), PixelToLogic(aPos), aName ))
4150
0
            {
4151
0
                if (bDragRect)          // Remove rectangle
4152
0
                {
4153
0
                    bDragRect = false;
4154
0
                    UpdateDragRectOverlay();
4155
0
                }
4156
4157
                //! highlight chart? (selection border?)
4158
4159
0
                sal_Int8 nRet = rEvt.mnAction;
4160
0
                return nRet;
4161
0
            }
4162
0
        }
4163
4164
0
        if (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) // whole sheet?
4165
0
        {
4166
0
            bool bOk = rThisDoc.IsDocEditable();
4167
0
            return bOk ? rEvt.mnAction : 0;                     // don't draw selection frame
4168
0
        }
4169
4170
0
        SCCOL  nPosX;
4171
0
        SCROW  nPosY;
4172
0
        mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4173
4174
0
        ScRange aSourceRange = rData.pCellTransfer->GetRange();
4175
0
        SCCOL nSourceStartX = aSourceRange.aStart.Col();
4176
0
        SCROW nSourceStartY = aSourceRange.aStart.Row();
4177
0
        SCCOL nSourceEndX = aSourceRange.aEnd.Col();
4178
0
        SCROW nSourceEndY = aSourceRange.aEnd.Row();
4179
0
        SCCOL nSizeX = nSourceEndX - nSourceStartX + 1;
4180
0
        SCROW nSizeY = nSourceEndY - nSourceStartY + 1;
4181
4182
0
        if ( rEvt.mnAction != DND_ACTION_MOVE )
4183
0
            nSizeY = rData.pCellTransfer->GetNonFilteredRows();     // copy/link: no filtered rows
4184
4185
0
        SCCOL nNewDragX = nPosX - rData.pCellTransfer->GetDragHandleX();
4186
0
        if (nNewDragX<0) nNewDragX=0;
4187
0
        if (nNewDragX+(nSizeX-1) > rThisDoc.MaxCol())
4188
0
            nNewDragX = rThisDoc.MaxCol()-(nSizeX-1);
4189
0
        SCROW nNewDragY = nPosY - rData.pCellTransfer->GetDragHandleY();
4190
0
        if (nNewDragY<0) nNewDragY=0;
4191
0
        if (nNewDragY+(nSizeY-1) > rThisDoc.MaxRow())
4192
0
            nNewDragY = rThisDoc.MaxRow()-(nSizeY-1);
4193
4194
        //  don't break scenario ranges, don't drop on filtered
4195
0
        SCTAB nTab = mrViewData.CurrentTabForData();
4196
0
        ScRange aDropRange = lcl_MakeDropRange( rThisDoc, nNewDragX, nNewDragY, nTab, aSourceRange );
4197
0
        if ( lcl_TestScenarioRedliningDrop( &rThisDoc, aDropRange ) ||
4198
0
             lcl_TestScenarioRedliningDrop( pSourceDoc, aSourceRange ) ||
4199
0
             ScViewUtil::HasFiltered( aDropRange, rThisDoc) )
4200
0
        {
4201
0
            if (bDragRect)
4202
0
            {
4203
0
                bDragRect = false;
4204
0
                UpdateDragRectOverlay();
4205
0
            }
4206
0
            return DND_ACTION_NONE;
4207
0
        }
4208
4209
0
        InsCellCmd eDragInsertMode = INS_NONE;
4210
0
        Window::PointerState aState = GetPointerState();
4211
4212
        // check for datapilot item sorting
4213
0
        ScDPObject* pDPObj = nullptr;
4214
0
        if ( &rThisDoc == pSourceDoc && ( pDPObj = rThisDoc.GetDPAtCursor( nNewDragX, nNewDragY, nTab ) ) != nullptr )
4215
0
        {
4216
            // drop on DataPilot table: sort or nothing
4217
4218
0
            bool bDPSort = false;
4219
0
            if ( rThisDoc.GetDPAtCursor( nSourceStartX, nSourceStartY, aSourceRange.aStart.Tab() ) == pDPObj )
4220
0
            {
4221
0
                sheet::DataPilotTableHeaderData aDestData;
4222
0
                pDPObj->GetHeaderPositionData( ScAddress(nNewDragX, nNewDragY, nTab), aDestData );
4223
0
                bool bValid = ( aDestData.Dimension >= 0 );        // dropping onto a field
4224
4225
                // look through the source range
4226
0
                for (SCROW nRow = aSourceRange.aStart.Row(); bValid && nRow <= aSourceRange.aEnd.Row(); ++nRow )
4227
0
                    for (SCCOL nCol = aSourceRange.aStart.Col(); bValid && nCol <= aSourceRange.aEnd.Col(); ++nCol )
4228
0
                    {
4229
0
                        sheet::DataPilotTableHeaderData aSourceData;
4230
0
                        pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, aSourceRange.aStart.Tab() ), aSourceData );
4231
0
                        if ( aSourceData.Dimension != aDestData.Dimension || aSourceData.MemberName.isEmpty() )
4232
0
                            bValid = false;     // empty (subtotal) or different field
4233
0
                    }
4234
4235
0
                if ( bValid )
4236
0
                {
4237
0
                    bool bIsDataLayout;
4238
0
                    OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
4239
0
                    const ScDPSaveDimension* pDim = pDPObj->GetSaveData()->GetExistingDimensionByName( aDimName );
4240
0
                    if ( pDim )
4241
0
                    {
4242
0
                        ScRange aOutRange = pDPObj->GetOutRange();
4243
4244
0
                        sheet::DataPilotFieldOrientation nOrient = pDim->GetOrientation();
4245
0
                        if ( nOrient == sheet::DataPilotFieldOrientation_COLUMN )
4246
0
                        {
4247
0
                            eDragInsertMode = INS_CELLSRIGHT;
4248
0
                            nSizeY = aOutRange.aEnd.Row() - nNewDragY + 1;
4249
0
                            bDPSort = true;
4250
0
                        }
4251
0
                        else if ( nOrient == sheet::DataPilotFieldOrientation_ROW )
4252
0
                        {
4253
0
                            eDragInsertMode = INS_CELLSDOWN;
4254
0
                            nSizeX = aOutRange.aEnd.Col() - nNewDragX + 1;
4255
0
                            bDPSort = true;
4256
0
                        }
4257
0
                    }
4258
0
                }
4259
0
            }
4260
4261
0
            if ( !bDPSort )
4262
0
            {
4263
                // no valid sorting in a DataPilot table -> disallow
4264
0
                if ( bDragRect )
4265
0
                {
4266
0
                    bDragRect = false;
4267
0
                    UpdateDragRectOverlay();
4268
0
                }
4269
0
                return DND_ACTION_NONE;
4270
0
            }
4271
0
        }
4272
0
        else if ( aState.mnState & KEY_MOD2 )
4273
0
        {
4274
0
            if ( &rThisDoc == pSourceDoc && nTab == aSourceRange.aStart.Tab() )
4275
0
            {
4276
0
                tools::Long nDeltaX = std::abs( static_cast< tools::Long >( nNewDragX - nSourceStartX ) );
4277
0
                tools::Long nDeltaY = std::abs( static_cast< tools::Long >( nNewDragY - nSourceStartY ) );
4278
0
                if ( nDeltaX <= nDeltaY )
4279
0
                {
4280
0
                    eDragInsertMode = INS_CELLSDOWN;
4281
0
                }
4282
0
                else
4283
0
                {
4284
0
                    eDragInsertMode = INS_CELLSRIGHT;
4285
0
                }
4286
4287
0
                if ( ( eDragInsertMode == INS_CELLSDOWN && nNewDragY <= nSourceEndY &&
4288
0
                       ( nNewDragX + nSizeX - 1 ) >= nSourceStartX && nNewDragX <= nSourceEndX &&
4289
0
                       ( nNewDragX != nSourceStartX || nNewDragY >= nSourceStartY ) ) ||
4290
0
                     ( eDragInsertMode == INS_CELLSRIGHT && nNewDragX <= nSourceEndX &&
4291
0
                       ( nNewDragY + nSizeY - 1 ) >= nSourceStartY && nNewDragY <= nSourceEndY &&
4292
0
                       ( nNewDragY != nSourceStartY || nNewDragX >= nSourceStartX ) ) )
4293
0
                {
4294
0
                    if ( bDragRect )
4295
0
                    {
4296
0
                        bDragRect = false;
4297
0
                        UpdateDragRectOverlay();
4298
0
                    }
4299
0
                    return DND_ACTION_NONE;
4300
0
                }
4301
0
            }
4302
0
            else
4303
0
            {
4304
0
                if ( static_cast< tools::Long >( nSizeX ) >= static_cast< tools::Long >( nSizeY ) )
4305
0
                {
4306
0
                    eDragInsertMode = INS_CELLSDOWN;
4307
4308
0
                }
4309
0
                else
4310
0
                {
4311
0
                    eDragInsertMode = INS_CELLSRIGHT;
4312
0
                }
4313
0
            }
4314
0
        }
4315
4316
0
        if ( nNewDragX != nDragStartX || nNewDragY != nDragStartY ||
4317
0
             nDragStartX+nSizeX-1 != nDragEndX || nDragStartY+nSizeY-1 != nDragEndY ||
4318
0
             !bDragRect || eDragInsertMode != meDragInsertMode )
4319
0
        {
4320
0
            nDragStartX = nNewDragX;
4321
0
            nDragStartY = nNewDragY;
4322
0
            nDragEndX = nDragStartX+nSizeX-1;
4323
0
            nDragEndY = nDragStartY+nSizeY-1;
4324
0
            bDragRect = true;
4325
0
            meDragInsertMode = eDragInsertMode;
4326
4327
0
            UpdateDragRectOverlay();
4328
0
        }
4329
0
    }
4330
4331
0
    return rEvt.mnAction;
4332
0
}
4333
4334
sal_Int8 ScGridWindow::AcceptDrop( const AcceptDropEvent& rEvt )
4335
0
{
4336
0
    const ScDragData* pData = ScModule::get()->GetDragData();
4337
0
    if (!pData)
4338
0
        return DND_ACTION_NONE;
4339
0
    const ScDragData& rData = *pData;
4340
0
    if ( rEvt.mbLeaving )
4341
0
    {
4342
0
        DrawMarkDropObj( nullptr );
4343
0
        return AcceptPrivateDrop( rEvt, rData );   // hide drop marker for internal D&D
4344
0
    }
4345
4346
0
    if ( mrViewData.GetDocShell()->IsReadOnly() )
4347
0
        return DND_ACTION_NONE;
4348
4349
0
    ScDocument& rThisDoc = mrViewData.GetDocument();
4350
0
    sal_Int8 nRet = DND_ACTION_NONE;
4351
4352
0
    if (rData.pCellTransfer)
4353
0
    {
4354
0
        ScRange aSource = rData.pCellTransfer->GetRange();
4355
0
        if ( aSource.aStart.Col() != 0 || aSource.aEnd.Col() != rThisDoc.MaxCol() ||
4356
0
             aSource.aStart.Row() != 0 || aSource.aEnd.Row() != rThisDoc.MaxRow() )
4357
0
            DropScroll( rEvt.maPosPixel );
4358
4359
0
        nRet = AcceptPrivateDrop( rEvt, rData );
4360
0
    }
4361
0
    else
4362
0
    {
4363
0
        if ( !rData.aLinkDoc.isEmpty() )
4364
0
        {
4365
0
            OUString aThisName;
4366
0
            ScDocShell* pDocSh = mrViewData.GetDocShell();
4367
0
            if (pDocSh && pDocSh->HasName())
4368
0
                aThisName = pDocSh->GetMedium()->GetName();
4369
4370
0
            if ( rData.aLinkDoc != aThisName )
4371
0
                nRet = rEvt.mnAction;
4372
0
        }
4373
0
        else if (!rData.aJumpTarget.isEmpty())
4374
0
        {
4375
            //  internal bookmarks (from Navigator)
4376
            //  local jumps from an unnamed document are possible only within a document
4377
4378
0
            if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
4379
0
                nRet = rEvt.mnAction;
4380
0
        }
4381
0
        else
4382
0
        {
4383
0
            sal_Int8 nMyAction = rEvt.mnAction;
4384
4385
            // clear DND_ACTION_LINK when other actions are set. The usage below cannot handle
4386
            // multiple set values
4387
0
            if((nMyAction & DND_ACTION_LINK) && (nMyAction & DND_ACTION_COPYMOVE))
4388
0
            {
4389
0
                nMyAction &= ~DND_ACTION_LINK;
4390
0
            }
4391
4392
0
            if ( !rData.pDrawTransfer ||
4393
0
                    !IsMyModel(rData.pDrawTransfer->GetDragSourceView()) )      // drawing within the document
4394
0
                if ( rEvt.mbDefault && nMyAction == DND_ACTION_MOVE )
4395
0
                    nMyAction = DND_ACTION_COPY;
4396
4397
0
            SdrObject* pHitObj = rThisDoc.GetObjectAtPoint(
4398
0
                        mrViewData.CurrentTabForData(), PixelToLogic(rEvt.maPosPixel) );
4399
0
            if ( pHitObj && nMyAction == DND_ACTION_LINK )
4400
0
            {
4401
0
                if ( IsDropFormatSupported(SotClipboardFormatId::SVXB)
4402
0
                    || IsDropFormatSupported(SotClipboardFormatId::GDIMETAFILE)
4403
0
                    || IsDropFormatSupported(SotClipboardFormatId::PNG)
4404
0
                    || IsDropFormatSupported(SotClipboardFormatId::BITMAP) )
4405
0
                {
4406
                    //  graphic dragged onto drawing object
4407
0
                    DrawMarkDropObj( pHitObj );
4408
0
                    nRet = nMyAction;
4409
0
                }
4410
0
            }
4411
0
            if (!nRet)
4412
0
            {
4413
0
                DrawMarkDropObj(nullptr);
4414
4415
0
                switch ( nMyAction )
4416
0
                {
4417
0
                    case DND_ACTION_COPY:
4418
0
                    case DND_ACTION_MOVE:
4419
0
                    case DND_ACTION_COPYMOVE:
4420
0
                        {
4421
0
                            bool bMove = ( nMyAction == DND_ACTION_MOVE );
4422
0
                            if ( IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE ) ||
4423
0
                                 IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
4424
0
                                 IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
4425
0
                                 IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
4426
0
                                 IsDropFormatSupported( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
4427
0
                                 IsDropFormatSupported( SotClipboardFormatId::STRING ) ||
4428
0
                                 IsDropFormatSupported( SotClipboardFormatId::STRING_TSVC ) ||
4429
0
                                 IsDropFormatSupported( SotClipboardFormatId::SYLK ) ||
4430
0
                                 IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
4431
0
                                 IsDropFormatSupported( SotClipboardFormatId::HTML ) ||
4432
0
                                 IsDropFormatSupported( SotClipboardFormatId::HTML_SIMPLE ) ||
4433
0
                                 IsDropFormatSupported( SotClipboardFormatId::DIF ) ||
4434
0
                                 IsDropFormatSupported( SotClipboardFormatId::DRAWING ) ||
4435
0
                                 IsDropFormatSupported( SotClipboardFormatId::SVXB ) ||
4436
0
                                 IsDropFormatSupported( SotClipboardFormatId::RTF ) ||
4437
0
                                 IsDropFormatSupported( SotClipboardFormatId::RICHTEXT ) ||
4438
0
                                 IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ) ||
4439
0
                                 IsDropFormatSupported( SotClipboardFormatId::PNG ) ||
4440
0
                                 IsDropFormatSupported( SotClipboardFormatId::BITMAP ) ||
4441
0
                                 IsDropFormatSupported( SotClipboardFormatId::SBA_DATAEXCHANGE ) ||
4442
0
                                 IsDropFormatSupported( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) ||
4443
0
                                 ( !bMove && (
4444
0
                                    IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
4445
0
                                     IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
4446
0
                                     IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
4447
0
                                     IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
4448
0
                                     IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
4449
0
                                     IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) ) ) )
4450
0
                            {
4451
0
                                nRet = nMyAction;
4452
0
                            }
4453
0
                        }
4454
0
                        break;
4455
0
                    case DND_ACTION_LINK:
4456
0
                        if ( IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
4457
0
                             IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
4458
0
                             IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
4459
0
                             IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
4460
0
                             IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
4461
0
                             IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
4462
0
                             IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
4463
0
                             IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
4464
0
                             IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4465
0
                        {
4466
0
                            nRet = nMyAction;
4467
0
                        }
4468
0
                        break;
4469
0
                }
4470
4471
0
                if ( nRet )
4472
0
                {
4473
                    // Simple check for protection: It's not known here if the drop will result
4474
                    // in cells or drawing objects (some formats can be both) and how many cells
4475
                    // the result will be. But if IsFormatEditable for the drop cell position
4476
                    // is sal_False (ignores matrix formulas), nothing can be pasted, so the drop
4477
                    // can already be rejected here.
4478
4479
0
                    Point aPos = rEvt.maPosPixel;
4480
0
                    SCCOL nPosX;
4481
0
                    SCROW nPosY;
4482
0
                    mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4483
0
                    SCTAB nTab = mrViewData.CurrentTabForData();
4484
0
                    ScDocument& rDoc = mrViewData.GetDocument();
4485
4486
0
                    ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, nPosX, nPosY, nPosX, nPosY);
4487
0
                    if ( !aTester.IsFormatEditable() )
4488
0
                        nRet = DND_ACTION_NONE;             // forbidden
4489
0
                }
4490
0
            }
4491
0
        }
4492
4493
        //  scroll only for accepted formats
4494
0
        if (nRet)
4495
0
            DropScroll( rEvt.maPosPixel );
4496
0
    }
4497
4498
0
    return nRet;
4499
0
}
4500
4501
static SotClipboardFormatId lcl_GetDropFormatId( const uno::Reference<datatransfer::XTransferable>& xTransfer, bool bPreferText )
4502
0
{
4503
0
    TransferableDataHelper aDataHelper( xTransfer );
4504
4505
0
    if ( !aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
4506
0
    {
4507
        //  use bookmark formats if no sba is present
4508
4509
0
        if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
4510
0
            return SotClipboardFormatId::SOLK;
4511
0
        else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
4512
0
            return SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
4513
0
        else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
4514
0
            return SotClipboardFormatId::NETSCAPE_BOOKMARK;
4515
        // In FILEGRPDESCRIPTOR, we only need web links (see ScViewFunc::PasteDataFormat).
4516
        // Check if the descriptor really contains them.
4517
0
        else if (aDataHelper.GetINetBookmark(SotClipboardFormatId::FILEGRPDESCRIPTOR,
4518
0
                                             o3tl::temporary(INetBookmark())))
4519
0
            return SotClipboardFormatId::FILEGRPDESCRIPTOR;
4520
0
    }
4521
4522
0
    SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
4523
0
    if ( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) )
4524
0
        nFormatId = SotClipboardFormatId::DRAWING;
4525
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
4526
0
        nFormatId = SotClipboardFormatId::SVXB;
4527
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) )
4528
0
    {
4529
        //  If it's a Writer object, insert RTF instead of OLE
4530
4531
0
        bool bDoRtf = false;
4532
0
        std::unique_ptr<SvStream> xStm;
4533
0
        TransferableObjectDescriptor aObjDesc;
4534
0
        if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) &&
4535
0
            (xStm = aDataHelper.GetSotStorageStream( SotClipboardFormatId::EMBED_SOURCE )) )
4536
0
        {
4537
0
            bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
4538
0
                         aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
4539
0
                       && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
4540
0
        }
4541
0
        if ( bDoRtf )
4542
0
            nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
4543
0
        else
4544
0
            nFormatId = SotClipboardFormatId::EMBED_SOURCE;
4545
0
    }
4546
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
4547
0
        nFormatId = SotClipboardFormatId::LINK_SOURCE;
4548
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
4549
0
        nFormatId = SotClipboardFormatId::SBA_DATAEXCHANGE;
4550
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) )
4551
0
        nFormatId = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
4552
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_12 ) )
4553
0
        nFormatId = SotClipboardFormatId::BIFF_12;
4554
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_8 ) )
4555
0
        nFormatId = SotClipboardFormatId::BIFF_8;
4556
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_5 ) )
4557
0
        nFormatId = SotClipboardFormatId::BIFF_5;
4558
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) )
4559
0
        nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
4560
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) )
4561
0
        nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
4562
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
4563
0
        nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
4564
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
4565
0
        nFormatId = SotClipboardFormatId::RTF;
4566
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
4567
0
        nFormatId = SotClipboardFormatId::RICHTEXT;
4568
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML ) )
4569
0
        nFormatId = SotClipboardFormatId::HTML;
4570
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) )
4571
0
        nFormatId = SotClipboardFormatId::HTML_SIMPLE;
4572
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SYLK ) )
4573
0
        nFormatId = SotClipboardFormatId::SYLK;
4574
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
4575
0
        nFormatId = SotClipboardFormatId::LINK;
4576
0
    else if ( bPreferText && aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) // #i86734# the behaviour introduced in #i62773# is wrong when pasting
4577
0
        nFormatId = SotClipboardFormatId::STRING;
4578
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
4579
0
        nFormatId = SotClipboardFormatId::FILE_LIST;
4580
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )    // #i62773# FILE_LIST/FILE before STRING (Unix file managers)
4581
0
        nFormatId = SotClipboardFormatId::SIMPLE_FILE;
4582
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING_TSVC ) )
4583
0
        nFormatId = SotClipboardFormatId::STRING_TSVC;
4584
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
4585
0
        nFormatId = SotClipboardFormatId::STRING;
4586
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
4587
0
        nFormatId = SotClipboardFormatId::GDIMETAFILE;
4588
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
4589
0
        nFormatId = SotClipboardFormatId::PNG;
4590
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) )
4591
0
        nFormatId = SotClipboardFormatId::BITMAP;
4592
4593
0
    return nFormatId;
4594
0
}
4595
4596
static SotClipboardFormatId lcl_GetDropLinkId( const uno::Reference<datatransfer::XTransferable>& xTransfer )
4597
0
{
4598
0
    TransferableDataHelper aDataHelper( xTransfer );
4599
4600
0
    SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
4601
0
    if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
4602
0
        nFormatId = SotClipboardFormatId::LINK_SOURCE;
4603
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
4604
0
        nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
4605
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
4606
0
        nFormatId = SotClipboardFormatId::LINK;
4607
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
4608
0
        nFormatId = SotClipboardFormatId::FILE_LIST;
4609
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )
4610
0
        nFormatId = SotClipboardFormatId::SIMPLE_FILE;
4611
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
4612
0
        nFormatId = SotClipboardFormatId::SOLK;
4613
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
4614
0
        nFormatId = SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
4615
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
4616
0
        nFormatId = SotClipboardFormatId::NETSCAPE_BOOKMARK;
4617
0
    else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4618
0
        nFormatId = SotClipboardFormatId::FILEGRPDESCRIPTOR;
4619
4620
0
    return nFormatId;
4621
0
}
4622
4623
sal_Int8 ScGridWindow::ExecutePrivateDrop( const ExecuteDropEvent& rEvt, const ScDragData& rData )
4624
0
{
4625
    // hide drop marker
4626
0
    bDragRect = false;
4627
0
    UpdateDragRectOverlay();
4628
4629
0
    return DropTransferObj( rData.pCellTransfer, nDragStartX, nDragStartY,
4630
0
                                PixelToLogic(rEvt.maPosPixel), rEvt.mnAction );
4631
0
}
4632
4633
sal_Int8 ScGridWindow::DropTransferObj( ScTransferObj* pTransObj, SCCOL nDestPosX, SCROW nDestPosY,
4634
                                        const Point& rLogicPos, sal_Int8 nDndAction )
4635
0
{
4636
0
    if ( !pTransObj )
4637
0
        return 0;
4638
4639
0
    ScDocument* pSourceDoc = pTransObj->GetSourceDocument();
4640
0
    ScDocShell* pDocSh     = mrViewData.GetDocShell();
4641
0
    ScDocument& rThisDoc   = mrViewData.GetDocument();
4642
0
    ScViewFunc* pView      = mrViewData.GetView();
4643
0
    SCTAB       nThisTab   = mrViewData.CurrentTabForData();
4644
0
    ScDragSrc   nFlags     = pTransObj->GetDragSourceFlags();
4645
4646
0
    bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
4647
0
    bool bIsMove = ( nDndAction == DND_ACTION_MOVE && !bIsNavi );
4648
4649
    // workaround for wrong nDndAction on Windows when pressing solely
4650
    // the Alt key during drag and drop;
4651
    // can be removed after #i79215# has been fixed
4652
0
    if ( meDragInsertMode != INS_NONE )
4653
0
    {
4654
0
        bIsMove = ( nDndAction & DND_ACTION_MOVE && !bIsNavi );
4655
0
    }
4656
4657
0
    bool bIsLink = ( nDndAction == DND_ACTION_LINK );
4658
4659
0
    ScRange aSource = pTransObj->GetRange();
4660
4661
    //  only use visible tab from source range - when dragging within one table,
4662
    //  all selected tables at the time of dropping are used (handled in MoveBlockTo)
4663
0
    SCTAB nSourceTab = pTransObj->GetVisibleTab();
4664
0
    aSource.aStart.SetTab( nSourceTab );
4665
0
    aSource.aEnd.SetTab( nSourceTab );
4666
4667
0
    SCCOL nSizeX = aSource.aEnd.Col() - aSource.aStart.Col() + 1;
4668
0
    SCROW nSizeY = (bIsMove ? (aSource.aEnd.Row() - aSource.aStart.Row() + 1) :
4669
0
            pTransObj->GetNonFilteredRows());   // copy/link: no filtered rows
4670
0
    ScRange aDest( nDestPosX, nDestPosY, nThisTab,
4671
0
                   nDestPosX + nSizeX - 1, nDestPosY + nSizeY - 1, nThisTab );
4672
4673
    /* NOTE: AcceptPrivateDrop() already checked for filtered conditions during
4674
     * dragging and adapted drawing of the selection frame. We check here
4675
     * (again) because this may actually also be called from PasteSelection(),
4676
     * we would have to duplicate determination of flags and destination range
4677
     * and would lose the context of the "filtered destination is OK" cases
4678
     * below, which is already awkward enough as is. */
4679
4680
    // Don't move filtered source.
4681
0
    bool bFiltered = (bIsMove && pTransObj->HasFilteredRows());
4682
0
    if (!bFiltered)
4683
0
    {
4684
0
        if (pSourceDoc != &rThisDoc && ((nFlags & ScDragSrc::Table) ||
4685
0
                    (!bIsLink && meDragInsertMode == INS_NONE)))
4686
0
        {
4687
            // Nothing. Either entire sheet to be dropped, or the one case
4688
            // where PasteFromClip() is to be called that handles a filtered
4689
            // destination itself. Drag-copy from another document without
4690
            // inserting cells.
4691
0
        }
4692
0
        else
4693
            // Don't copy or move to filtered destination.
4694
0
            bFiltered = ScViewUtil::HasFiltered(aDest, rThisDoc);
4695
0
    }
4696
4697
0
    bool bDone = false;
4698
4699
0
    if (!bFiltered && pSourceDoc == &rThisDoc)
4700
0
    {
4701
0
        if (nFlags & ScDragSrc::Table) // whole sheet?
4702
0
        {
4703
0
            if ( rThisDoc.IsDocEditable() )
4704
0
            {
4705
0
                SCTAB nSrcTab = aSource.aStart.Tab();
4706
0
                mrViewData.GetDocShell()->MoveTable( nSrcTab, nThisTab, !bIsMove, true );   // with Undo
4707
0
                pView->SetTabNo( nThisTab, true );
4708
0
                bDone = true;
4709
0
            }
4710
0
        }
4711
0
        else                                        // move/copy block
4712
0
        {
4713
0
            OUString aChartName;
4714
0
            if (rThisDoc.HasChartAtPoint( nThisTab, rLogicPos, aChartName ))
4715
0
            {
4716
0
                OUString aRangeName(aSource.Format(rThisDoc, ScRefFlags::RANGE_ABS_3D,
4717
0
                            rThisDoc.GetAddressConvention()));
4718
0
                SfxStringItem aNameItem( SID_CHART_NAME, aChartName );
4719
0
                SfxStringItem aRangeItem( SID_CHART_SOURCE, aRangeName );
4720
0
                sal_uInt16 nId = bIsMove ? SID_CHART_SOURCE : SID_CHART_ADDSOURCE;
4721
0
                mrViewData.GetDispatcher().ExecuteList(nId,
4722
0
                        SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
4723
0
                        { &aRangeItem, &aNameItem });
4724
0
                bDone = true;
4725
0
            }
4726
0
            else if ( rThisDoc.GetDPAtCursor( nDestPosX, nDestPosY, nThisTab ) )
4727
0
            {
4728
                // drop on DataPilot table: try to sort, fail if that isn't possible
4729
4730
0
                ScAddress aDestPos( nDestPosX, nDestPosY, nThisTab );
4731
0
                if ( aDestPos != aSource.aStart )
4732
0
                    bDone = mrViewData.GetView()->DataPilotMove( aSource, aDestPos );
4733
0
                else
4734
0
                    bDone = true;   // same position: nothing
4735
0
            }
4736
0
            else if ( nDestPosX != aSource.aStart.Col() || nDestPosY != aSource.aStart.Row() ||
4737
0
                        nSourceTab != nThisTab )
4738
0
            {
4739
0
                OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
4740
0
                pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4741
4742
0
                SCCOL nCorrectCursorPosCol = 0;
4743
0
                SCROW nCorrectCursorPosRow = 0;
4744
4745
0
                bDone = true;
4746
0
                if ( meDragInsertMode != INS_NONE )
4747
0
                {
4748
                    // call with bApi = sal_True to avoid error messages in drop handler
4749
0
                    bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4750
0
                    if ( bDone )
4751
0
                    {
4752
0
                        if ( nThisTab == nSourceTab )
4753
0
                        {
4754
0
                            if ( meDragInsertMode == INS_CELLSDOWN &&
4755
0
                                 nDestPosX == aSource.aStart.Col() && nDestPosY < aSource.aStart.Row() )
4756
0
                            {
4757
0
                                ScRange aErrorRange( ScAddress::UNINITIALIZED );
4758
0
                                bDone = aSource.Move( 0, nSizeY, 0, aErrorRange, *pSourceDoc );
4759
0
                                nCorrectCursorPosRow = nSizeY;
4760
0
                            }
4761
0
                            else if ( meDragInsertMode == INS_CELLSRIGHT &&
4762
0
                                      nDestPosY == aSource.aStart.Row() && nDestPosX < aSource.aStart.Col() )
4763
0
                            {
4764
0
                                ScRange aErrorRange( ScAddress::UNINITIALIZED );
4765
0
                                bDone = aSource.Move( nSizeX, 0, 0, aErrorRange, *pSourceDoc );
4766
0
                                nCorrectCursorPosCol = nSizeX;
4767
0
                            }
4768
0
                        }
4769
0
                        pDocSh->UpdateOle(mrViewData);
4770
0
                        pView->CellContentChanged();
4771
0
                    }
4772
0
                }
4773
4774
0
                if ( bDone )
4775
0
                {
4776
0
                    if ( bIsLink )
4777
0
                    {
4778
0
                        bDone = pView->LinkBlock( aSource, aDest.aStart );
4779
0
                    }
4780
0
                    else
4781
0
                    {
4782
0
                        bDone = pView->MoveBlockTo( aSource, aDest.aStart, bIsMove );
4783
0
                    }
4784
0
                }
4785
4786
0
                if ( bDone && meDragInsertMode != INS_NONE && bIsMove && nThisTab == nSourceTab )
4787
0
                {
4788
0
                    DelCellCmd eCmd = DelCellCmd::NONE;
4789
0
                    if ( meDragInsertMode == INS_CELLSDOWN )
4790
0
                    {
4791
0
                        eCmd = DelCellCmd::CellsUp;
4792
0
                    }
4793
0
                    else if ( meDragInsertMode == INS_CELLSRIGHT )
4794
0
                    {
4795
0
                        eCmd = DelCellCmd::CellsLeft;
4796
0
                    }
4797
4798
0
                    if ( ( eCmd == DelCellCmd::CellsUp  && nDestPosX == aSource.aStart.Col() ) ||
4799
0
                         ( eCmd == DelCellCmd::CellsLeft && nDestPosY == aSource.aStart.Row() ) )
4800
0
                    {
4801
                        // call with bApi = sal_True to avoid error messages in drop handler
4802
0
                        bDone = pDocSh->GetDocFunc().DeleteCells( aSource, nullptr, eCmd, true /*bApi*/ );
4803
0
                        if ( bDone )
4804
0
                        {
4805
0
                            if ( eCmd == DelCellCmd::CellsUp && nDestPosY > aSource.aEnd.Row() )
4806
0
                            {
4807
0
                                ScRange aErrorRange( ScAddress::UNINITIALIZED );
4808
0
                                bDone = aDest.Move( 0, -nSizeY, 0, aErrorRange, rThisDoc );
4809
0
                            }
4810
0
                            else if ( eCmd == DelCellCmd::CellsLeft && nDestPosX > aSource.aEnd.Col() )
4811
0
                            {
4812
0
                                ScRange aErrorRange( ScAddress::UNINITIALIZED );
4813
0
                                bDone = aDest.Move( -nSizeX, 0, 0, aErrorRange, rThisDoc );
4814
0
                            }
4815
0
                            pDocSh->UpdateOle(mrViewData);
4816
0
                            pView->CellContentChanged();
4817
0
                        }
4818
0
                    }
4819
0
                }
4820
4821
0
                if ( bDone )
4822
0
                {
4823
0
                    pView->MarkRange( aDest, false );
4824
4825
0
                    SCCOL nDCol;
4826
0
                    SCROW nDRow;
4827
0
                    if (pTransObj->WasSourceCursorInSelection())
4828
0
                    {
4829
0
                        nDCol = pTransObj->GetSourceCursorX() - aSource.aStart.Col() + nCorrectCursorPosCol;
4830
0
                        nDRow = pTransObj->GetSourceCursorY() - aSource.aStart.Row() + nCorrectCursorPosRow;
4831
0
                    }
4832
0
                    else
4833
0
                    {
4834
0
                        nDCol = 0;
4835
0
                        nDRow = 0;
4836
0
                    }
4837
0
                    pView->SetCursor( aDest.aStart.Col() + nDCol, aDest.aStart.Row() + nDRow );
4838
0
                }
4839
4840
0
                pDocSh->GetUndoManager()->LeaveListAction();
4841
4842
0
            }
4843
0
            else
4844
0
                bDone = true;       // nothing to do
4845
0
        }
4846
4847
0
        if (bDone)
4848
0
            pTransObj->SetDragWasInternal();    // don't delete source in DragFinished
4849
0
    }
4850
0
    else if ( !bFiltered && pSourceDoc )                        // between documents
4851
0
    {
4852
0
        if (nFlags & ScDragSrc::Table)          // copy/link sheets between documents
4853
0
        {
4854
0
            if ( rThisDoc.IsDocEditable() )
4855
0
            {
4856
0
                ScDocShell* pSrcShell = pTransObj->GetSourceDocShell();
4857
4858
0
                std::vector<SCTAB> nTabs;
4859
4860
0
                ScMarkData  aMark       = pTransObj->GetSourceMarkData();
4861
0
                SCTAB       nTabCount   = pSourceDoc->GetTableCount();
4862
4863
0
                for(SCTAB i=0; i<nTabCount; i++)
4864
0
                {
4865
0
                    if(aMark.GetTableSelect(i))
4866
0
                    {
4867
0
                        nTabs.push_back(i);
4868
0
                        for(SCTAB j=i+1;j<nTabCount;j++)
4869
0
                        {
4870
0
                            if((!pSourceDoc->IsVisible(j))&&(pSourceDoc->IsScenario(j)))
4871
0
                            {
4872
0
                                nTabs.push_back( j );
4873
0
                                i=j;
4874
0
                            }
4875
0
                            else break;
4876
0
                        }
4877
0
                    }
4878
0
                }
4879
4880
0
                pView->ImportTables( pSrcShell,static_cast<SCTAB>(nTabs.size()), nTabs.data(), bIsLink, nThisTab );
4881
0
                bDone = true;
4882
0
            }
4883
0
        }
4884
0
        else if ( bIsLink )
4885
0
        {
4886
            //  as in PasteDDE
4887
            //  (external references might be used instead?)
4888
4889
0
            ScDocShell* pSourceSh = pSourceDoc->GetDocumentShell();
4890
0
            OSL_ENSURE(pSourceSh, "drag document has no shell");
4891
0
            if (pSourceSh)
4892
0
            {
4893
0
                OUString aUndo = ScResId( STR_UNDO_COPY );
4894
0
                pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4895
4896
0
                bDone = true;
4897
0
                if ( meDragInsertMode != INS_NONE )
4898
0
                {
4899
                    // call with bApi = sal_True to avoid error messages in drop handler
4900
0
                    bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4901
0
                    if ( bDone )
4902
0
                    {
4903
0
                        pDocSh->UpdateOle(mrViewData);
4904
0
                        pView->CellContentChanged();
4905
0
                    }
4906
0
                }
4907
4908
0
                if ( bDone )
4909
0
                {
4910
0
                    OUString aApp = Application::GetAppName();
4911
0
                    OUString aTopic = pSourceSh->GetTitle( SFX_TITLE_FULLNAME );
4912
0
                    OUString aItem(aSource.Format(*pSourceDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
4913
4914
                    // TODO: we could define ocQuote for "
4915
0
                    const OUString aQuote('"');
4916
0
                    const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
4917
0
                    OUString aFormula =
4918
0
                            "=" +
4919
0
                            ScCompiler::GetNativeSymbol(ocDde) +
4920
0
                            ScCompiler::GetNativeSymbol(ocOpen) +
4921
0
                            aQuote +
4922
0
                            aApp +
4923
0
                            aQuote +
4924
0
                            sSep +
4925
0
                            aQuote +
4926
0
                            aTopic +
4927
0
                            aQuote +
4928
0
                            sSep +
4929
0
                            aQuote +
4930
0
                            aItem +
4931
0
                            aQuote +
4932
0
                            ScCompiler::GetNativeSymbol(ocClose);
4933
4934
0
                    pView->DoneBlockMode();
4935
0
                    pView->InitBlockMode( nDestPosX, nDestPosY, nThisTab );
4936
0
                    pView->MarkCursor( nDestPosX + nSizeX - 1,
4937
0
                                       nDestPosY + nSizeY - 1, nThisTab );
4938
4939
0
                    pView->EnterMatrix( aFormula, ::formula::FormulaGrammar::GRAM_NATIVE );
4940
4941
0
                    pView->MarkRange( aDest, false );
4942
0
                    pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
4943
0
                }
4944
4945
0
                pDocSh->GetUndoManager()->LeaveListAction();
4946
0
            }
4947
0
        }
4948
0
        else
4949
0
        {
4950
            //! HasSelectedBlockMatrixFragment without selected sheet?
4951
            //! or don't start dragging on a part of a matrix
4952
4953
0
            OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
4954
0
            pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4955
4956
0
            bDone = true;
4957
0
            if ( meDragInsertMode != INS_NONE )
4958
0
            {
4959
                // call with bApi = sal_True to avoid error messages in drop handler
4960
0
                bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4961
0
                if ( bDone )
4962
0
                {
4963
0
                    pDocSh->UpdateOle(mrViewData);
4964
0
                    pView->CellContentChanged();
4965
0
                }
4966
0
            }
4967
4968
0
            if ( bDone )
4969
0
            {
4970
0
                pView->Unmark();  // before SetCursor, so CheckSelectionTransfer isn't called with a selection
4971
0
                pView->SetCursor( nDestPosX, nDestPosY );
4972
0
                bDone = pView->PasteFromClip( InsertDeleteFlags::ALL, pTransObj->GetDocument() );  // clip-doc
4973
0
                if ( bDone )
4974
0
                {
4975
0
                    pView->MarkRange( aDest, false );
4976
0
                    pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
4977
0
                }
4978
0
            }
4979
4980
0
            pDocSh->GetUndoManager()->LeaveListAction();
4981
4982
            //  no longer call ResetMark here - the inserted block has been selected
4983
            //  and may have been copied to primary selection
4984
0
        }
4985
0
    }
4986
4987
0
    sal_Int8 nRet = bDone ? nDndAction : DND_ACTION_NONE;
4988
0
    return nRet;
4989
0
}
4990
4991
sal_Int8 ScGridWindow::ExecuteDrop( const ExecuteDropEvent& rEvt )
4992
0
{
4993
0
    DrawMarkDropObj( nullptr );    // drawing layer
4994
4995
0
    ScModule* pScMod = ScModule::get();
4996
0
    const ScDragData* pData = pScMod->GetDragData();
4997
0
    if (!pData)
4998
0
        return DND_ACTION_NONE;
4999
0
    const ScDragData& rData = *pData;
5000
0
    if (rData.pCellTransfer)
5001
0
        return ExecutePrivateDrop( rEvt, rData );
5002
5003
0
    Point aPos = rEvt.maPosPixel;
5004
5005
0
    if ( !rData.aLinkDoc.isEmpty() )
5006
0
    {
5007
        //  try to insert a link
5008
5009
0
        bool bOk = true;
5010
0
        OUString aThisName;
5011
0
        ScDocShell* pDocSh = mrViewData.GetDocShell();
5012
0
        if (pDocSh && pDocSh->HasName())
5013
0
            aThisName = pDocSh->GetMedium()->GetName();
5014
5015
0
        if ( rData.aLinkDoc == aThisName )              // error - no link within a document
5016
0
            bOk = false;
5017
0
        else
5018
0
        {
5019
0
            ScViewFunc* pView = mrViewData.GetView();
5020
0
            if ( !rData.aLinkTable.isEmpty() )
5021
0
                pView->InsertTableLink( rData.aLinkDoc, OUString(), OUString(),
5022
0
                                        rData.aLinkTable );
5023
0
            else if ( !rData.aLinkArea.isEmpty() )
5024
0
            {
5025
0
                SCCOL  nPosX;
5026
0
                SCROW  nPosY;
5027
0
                mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
5028
0
                pView->MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
5029
5030
0
                pView->InsertAreaLink( rData.aLinkDoc, OUString(), OUString(),
5031
0
                                        rData.aLinkArea );
5032
0
            }
5033
0
            else
5034
0
            {
5035
0
                OSL_FAIL("drop with link: no sheet nor area");
5036
0
                bOk = false;
5037
0
            }
5038
0
        }
5039
5040
0
        return bOk ? rEvt.mnAction : DND_ACTION_NONE;           // don't try anything else
5041
0
    }
5042
5043
0
    Point aLogicPos = PixelToLogic(aPos);
5044
0
    bool bIsLink = ( rEvt.mnAction == DND_ACTION_LINK );
5045
5046
0
    if (!bIsLink && rData.pDrawTransfer)
5047
0
    {
5048
0
        ScDragSrc nFlags = rData.pDrawTransfer->GetDragSourceFlags();
5049
5050
0
        bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
5051
0
        bool bIsMove = ( rEvt.mnAction == DND_ACTION_MOVE && !bIsNavi );
5052
5053
0
        bPasteIsMove = bIsMove;
5054
5055
0
        mrViewData.GetView()->PasteDraw(
5056
0
            aLogicPos, rData.pDrawTransfer->GetModel(), false, u"A", u"B");
5057
5058
0
        if (bPasteIsMove)
5059
0
            rData.pDrawTransfer->SetDragWasInternal();
5060
0
        bPasteIsMove = false;
5061
5062
0
        return rEvt.mnAction;
5063
0
    }
5064
5065
0
    SCCOL  nPosX;
5066
0
    SCROW  nPosY;
5067
0
    mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
5068
5069
0
    if (!rData.aJumpTarget.isEmpty())
5070
0
    {
5071
        //  internal bookmark (from Navigator)
5072
        //  bookmark clipboard formats are in PasteScDataObject
5073
5074
0
        if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
5075
0
        {
5076
0
            mrViewData.GetViewShell()->InsertBookmark( rData.aJumpText, rData.aJumpTarget,
5077
0
                                                        nPosX, nPosY );
5078
0
            return rEvt.mnAction;
5079
0
        }
5080
0
    }
5081
5082
0
    ScDocument& rThisDoc = mrViewData.GetDocument();
5083
0
    SdrObject* pHitObj = rThisDoc.GetObjectAtPoint( mrViewData.CurrentTabForData(), PixelToLogic(aPos) );
5084
0
    if ( pHitObj && bIsLink )
5085
0
    {
5086
        //  dropped on drawing object
5087
        //  PasteOnDrawObjectLinked checks for valid formats
5088
0
        if ( mrViewData.GetView()->PasteOnDrawObjectLinked( rEvt.maDropEvent.Transferable, *pHitObj ) )
5089
0
            return rEvt.mnAction;
5090
0
    }
5091
5092
0
    bool bDone = false;
5093
5094
0
    SotClipboardFormatId nFormatId = bIsLink ?
5095
0
                        lcl_GetDropLinkId( rEvt.maDropEvent.Transferable ) :
5096
0
                        lcl_GetDropFormatId( rEvt.maDropEvent.Transferable, false );
5097
0
    if ( nFormatId != SotClipboardFormatId::NONE )
5098
0
    {
5099
0
        pScMod->SetInExecuteDrop( true );   // #i28468# prevent error messages from PasteDataFormat
5100
0
        bDone = mrViewData.GetView()->PasteDataFormat(
5101
0
                    nFormatId, rEvt.maDropEvent.Transferable, nPosX, nPosY, &aLogicPos, bIsLink );
5102
0
        pScMod->SetInExecuteDrop( false );
5103
0
    }
5104
5105
0
    sal_Int8 nRet = bDone ? rEvt.mnAction : DND_ACTION_NONE;
5106
0
    return nRet;
5107
0
}
5108
5109
void ScGridWindow::PasteSelection( const Point& rPosPixel )
5110
0
{
5111
0
    Point aLogicPos = PixelToLogic( rPosPixel );
5112
5113
0
    SCCOL  nPosX;
5114
0
    SCROW  nPosY;
5115
0
    mrViewData.GetPosFromPixel( rPosPixel.X(), rPosPixel.Y(), eWhich, nPosX, nPosY );
5116
5117
    // If the mouse down was inside a visible note window, ignore it and
5118
    // leave it up to the ScPostIt to handle it
5119
0
    SdrView* pDrawView = mrViewData.GetViewShell()->GetScDrawView();
5120
0
    if (pDrawView)
5121
0
    {
5122
0
        const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList();
5123
0
        const size_t nCount = rMarkList.GetMarkCount();
5124
0
        for (size_t i = 0; i < nCount; ++i)
5125
0
        {
5126
0
            SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
5127
0
            if (pObj && pObj->GetLogicRect().Contains(aLogicPos))
5128
0
            {
5129
                // Inside an active drawing object.  Bail out.
5130
0
                return;
5131
0
            }
5132
0
        }
5133
0
    }
5134
5135
0
    ScSelectionTransferObj* pOwnSelection = ScModule::get()->GetSelectionTransfer();
5136
0
    if ( pOwnSelection )
5137
0
    {
5138
        //  within Calc
5139
5140
        // keep a reference to the data in case the selection is changed during paste
5141
0
        rtl::Reference<ScTransferObj> pCellTransfer = pOwnSelection->GetCellData();
5142
0
        if ( pCellTransfer )
5143
0
        {
5144
0
            DropTransferObj( pCellTransfer.get(), nPosX, nPosY, aLogicPos, DND_ACTION_COPY );
5145
0
        }
5146
0
        else
5147
0
        {
5148
            // keep a reference to the data in case the selection is changed during paste
5149
0
            rtl::Reference<ScDrawTransferObj> pDrawTransfer = pOwnSelection->GetDrawData();
5150
0
            if ( pDrawTransfer )
5151
0
            {
5152
                //  bSameDocClipboard argument for PasteDraw is needed
5153
                //  because only DragData is checked directly inside PasteDraw
5154
0
                mrViewData.GetView()->PasteDraw(
5155
0
                    aLogicPos, pDrawTransfer->GetModel(), false,
5156
0
                    pDrawTransfer->GetShellID(), SfxObjectShell::CreateShellID(mrViewData.GetDocShell()));
5157
0
            }
5158
0
        }
5159
0
    }
5160
0
    else
5161
0
    {
5162
        //  get selection from system
5163
0
        TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromPrimarySelection());
5164
0
        const uno::Reference<datatransfer::XTransferable>& xTransferable = aDataHelper.GetTransferable();
5165
0
        if ( xTransferable.is() )
5166
0
        {
5167
0
            SotClipboardFormatId nFormatId = lcl_GetDropFormatId( xTransferable, true );
5168
0
            if ( nFormatId != SotClipboardFormatId::NONE )
5169
0
                mrViewData.GetView()->PasteDataFormat( nFormatId, xTransferable, nPosX, nPosY, &aLogicPos );
5170
0
        }
5171
0
    }
5172
0
}
5173
5174
void ScGridWindow::UpdateEditViewPos()
5175
0
{
5176
0
    if (!mrViewData.HasEditView(eWhich))
5177
0
        return;
5178
5179
0
    EditView* pView;
5180
0
    SCCOL nCol;
5181
0
    SCROW nRow;
5182
0
    mrViewData.GetEditView( eWhich, pView, nCol, nRow );
5183
0
    SCCOL nEndCol = mrViewData.GetEditEndCol();
5184
0
    SCROW nEndRow = mrViewData.GetEditEndRow();
5185
5186
    //  hide EditView?
5187
5188
0
    bool bHide = ( nEndCol<mrViewData.GetPosX(eHWhich) || nEndRow<mrViewData.GetPosY(eVWhich) );
5189
5190
0
    if (ScModule::get()->IsFormulaMode() && mrViewData.GetTabNumber() != mrViewData.GetRefTabNo())
5191
0
        bHide = true;
5192
5193
5194
0
    if (bHide)
5195
0
    {
5196
0
        tools::Rectangle aRect = pView->GetOutputArea();
5197
0
        tools::Long nHeight = aRect.Bottom() - aRect.Top();
5198
0
        aRect.SetTop( PixelToLogic(GetOutputSizePixel(), mrViewData.GetLogicMode()).
5199
0
                        Height() * 2 );
5200
0
        aRect.SetBottom( aRect.Top() + nHeight );
5201
0
        pView->SetOutputArea( aRect );
5202
0
        pView->HideCursor();
5203
0
    }
5204
0
    else
5205
0
    {
5206
        // bForceToTop = sal_True for editing
5207
0
        tools::Rectangle aPixRect = mrViewData.GetEditArea( eWhich, nCol, nRow, this, nullptr, true );
5208
5209
0
        if (comphelper::LibreOfficeKit::isActive() &&
5210
0
            comphelper::LibreOfficeKit::isCompatFlagSet(
5211
0
                comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
5212
0
        {
5213
0
            tools::Rectangle aPTwipsRect = mrViewData.GetEditArea(eWhich, nCol, nRow, this, nullptr,
5214
0
                    true, true /* bInPrintTwips */);
5215
0
            tools::Rectangle aOutputAreaPTwips = pView->GetLOKSpecialOutputArea();
5216
0
            aOutputAreaPTwips.SetPos(aPTwipsRect.TopLeft());
5217
0
            pView->SetLOKSpecialOutputArea(aOutputAreaPTwips);
5218
0
        }
5219
5220
0
        Point aScrPos = PixelToLogic( aPixRect.TopLeft(), mrViewData.GetLogicMode() );
5221
5222
0
        tools::Rectangle aRect = pView->GetOutputArea();
5223
0
        aRect.SetPos( aScrPos );
5224
0
        pView->SetOutputArea( aRect );
5225
0
        pView->ShowCursor();
5226
0
    }
5227
0
}
5228
5229
void ScGridWindow::ScrollPixel( tools::Long nDifX, tools::Long nDifY )
5230
0
{
5231
0
    ClickExtern();
5232
0
    HideNoteOverlay();
5233
5234
0
    SetMapMode(MapMode(MapUnit::MapPixel));
5235
0
    Scroll( nDifX, nDifY, ScrollFlags::Children );
5236
0
    SetMapMode( GetDrawMapMode() );             // generated shifted MapMode
5237
5238
0
    UpdateEditViewPos();
5239
5240
0
    DrawAfterScroll();
5241
0
}
5242
5243
// Update Formulas ------------------------------------------------------
5244
5245
void ScGridWindow::UpdateFormulas(SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2)
5246
0
{
5247
0
    if (mrViewData.GetView()->IsMinimized())
5248
0
        return;
5249
5250
0
    if ( nPaintCount )
5251
0
    {
5252
        // Do not start, switched to paint
5253
        //  (then at least the MapMode would no longer be right)
5254
0
        if (!bNeedsRepaint)
5255
0
        {
5256
0
            bNeedsRepaint = true;           // -> at end of paint run Invalidate on all
5257
0
            ScDocument::EnsureIdleUpdate();
5258
0
        }
5259
0
        aRepaintPixel = tools::Rectangle();    // All
5260
0
        return;
5261
0
    }
5262
5263
0
    if ( comphelper::LibreOfficeKit::isActive() )
5264
0
    {
5265
0
        ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5266
5267
0
        if (nX1 < 0)
5268
0
            nX1 = pViewShell->GetLOKStartHeaderCol() + 1;
5269
0
        if (nY1 < 0)
5270
0
            nY1 = pViewShell->GetLOKStartHeaderRow() + 1;
5271
0
        if (nX2 < 0)
5272
0
            nX2 = pViewShell->GetLOKEndHeaderCol();
5273
0
        if (nY2 < 0)
5274
0
            nY2 = pViewShell->GetLOKEndHeaderRow();
5275
5276
0
        if (nX1 < 0 || nY1 < 0) return;
5277
5278
        // Consider frozen ranges not in main pane range as candidates
5279
        // for update
5280
0
        SCCOLROW nFreezeCol = mrViewData.GetLOKSheetFreezeIndex(true);
5281
0
        SCCOLROW nFreezeRow = mrViewData.GetLOKSheetFreezeIndex(false);
5282
0
        if ((nFreezeCol || nFreezeRow) && (nX1 || nY1))
5283
0
        {
5284
            // top left
5285
0
            if (nFreezeCol && nFreezeRow)
5286
0
                UpdateFormulaRange(0, 0, nFreezeCol, nFreezeRow);
5287
            // bottom left
5288
0
            if (nFreezeCol && nX1)
5289
0
                UpdateFormulaRange(0, nY1, nFreezeCol, nY2);
5290
            // top right
5291
0
            if (nFreezeRow && nY1)
5292
0
                UpdateFormulaRange(nX1, 0, nX2, nFreezeRow);
5293
0
        }
5294
0
    }
5295
0
    else
5296
0
    {
5297
0
        nX1 = mrViewData.GetPosX( eHWhich );
5298
0
        nY1 = mrViewData.GetPosY( eVWhich );
5299
0
        nX2 = nX1 + mrViewData.VisibleCellsX( eHWhich );
5300
0
        nY2 = nY1 + mrViewData.VisibleCellsY( eVWhich );
5301
0
    }
5302
5303
0
    UpdateFormulaRange(nX1, nY1, nX2, nY2);
5304
0
}
5305
5306
void ScGridWindow::UpdateFormulaRange(SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2)
5307
0
{
5308
0
    if (nX2 < nX1) nX2 = nX1;
5309
0
    if (nY2 < nY1) nY2 = nY1;
5310
5311
0
    ScDocument& rDoc = mrViewData.GetDocument();
5312
5313
0
    if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
5314
0
    if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();
5315
5316
    // Draw( nX1, nY1, nX2, nY2, SC_UPDATE_CHANGED );
5317
5318
    // don't draw directly - instead use OutputData to find changed area and invalidate
5319
5320
0
    SCROW nPosY = nY1;
5321
5322
0
    SCTAB nTab = mrViewData.CurrentTabForData();
5323
5324
0
    if ( !comphelper::LibreOfficeKit::isActive() )
5325
0
    {
5326
0
        rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
5327
0
    }
5328
5329
0
    Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
5330
0
    tools::Long nMirrorWidth = GetSizePixel().Width();
5331
0
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
5332
0
    if ( bLayoutRTL )
5333
0
    {
5334
0
        tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, nPosY, eWhich ).X();
5335
0
        nMirrorWidth = aScrPos.X() - nEndPixel;
5336
0
        aScrPos.setX( nEndPixel + 1 );
5337
0
    }
5338
5339
0
    tools::Long nScrX = aScrPos.X();
5340
0
    tools::Long nScrY = aScrPos.Y();
5341
5342
0
    double nPPTX = mrViewData.GetPPTX();
5343
0
    double nPPTY = mrViewData.GetPPTY();
5344
5345
0
    ScTableInfo aTabInfo(nY1, nY2, true);
5346
0
    rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab, nPPTX, nPPTY, false, false );
5347
5348
0
    double fZoomX = mrViewData.GetZoomX();
5349
0
    double fZoomY = mrViewData.GetZoomY();
5350
0
    ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
5351
0
                                nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
5352
0
                                &fZoomX, &fZoomY );
5353
0
    aOutputData.SetMirrorWidth( nMirrorWidth );
5354
5355
0
    aOutputData.FindChanged();
5356
5357
    // #i122149# do not use old GetChangedArea() which used polygon-based Regions, but use
5358
    // the region-band based new version; anyways, only rectangles are added
5359
0
    vcl::Region aChangedRegion( aOutputData.GetChangedAreaRegion() );   // logic (PixelToLogic)
5360
0
    if(!aChangedRegion.IsEmpty())
5361
0
    {
5362
0
        Invalidate(aChangedRegion);
5363
0
    }
5364
5365
0
    CheckNeedsRepaint();    // #i90362# used to be called via Draw() - still needed here
5366
0
}
5367
5368
void ScGridWindow::UpdateAutoFillMark(bool bMarked, const ScRange& rMarkRange)
5369
0
{
5370
0
    if ( bMarked != bAutoMarkVisible || ( bMarked && rMarkRange.aEnd != aAutoMarkPos ) )
5371
0
    {
5372
0
        bAutoMarkVisible = bMarked;
5373
0
        if ( bMarked )
5374
0
            aAutoMarkPos = rMarkRange.aEnd;
5375
5376
0
        UpdateAutoFillOverlay();
5377
0
    }
5378
0
}
5379
5380
void ScGridWindow::updateLOKInputHelp(const OUString& title, const OUString& content) const
5381
0
{
5382
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5383
5384
0
    boost::property_tree::ptree aTree;
5385
0
    aTree.put("title", title);
5386
0
    aTree.put("content", content);
5387
5388
0
    std::stringstream aStream;
5389
0
    boost::property_tree::write_json(aStream, aTree);
5390
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_INPUT_HELP, OString(aStream.str()));
5391
0
}
5392
5393
void ScGridWindow::updateLOKValListButton( bool bVisible, const ScAddress& rPos ) const
5394
0
{
5395
0
    SCCOL nX = rPos.Col();
5396
0
    SCROW nY = rPos.Row();
5397
0
    std::stringstream ss;
5398
0
    ss << nX << ", " << nY << ", " << static_cast<unsigned int>(bVisible);
5399
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5400
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_LIST_BUTTON, OString(ss.str()));
5401
0
}
5402
5403
void ScGridWindow::notifyKitCellFollowJump( ) const
5404
0
{
5405
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5406
5407
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SC_FOLLOW_JUMP, getCellCursor());
5408
0
}
5409
5410
void ScGridWindow::UpdateListValPos( bool bVisible, const ScAddress& rPos )
5411
0
{
5412
0
    bool bOldButton = bListValButton;
5413
0
    ScAddress aOldPos = aListValPos;
5414
5415
0
    bListValButton = bVisible;
5416
0
    aListValPos = rPos;
5417
5418
0
    if ( bListValButton )
5419
0
    {
5420
0
        if ( !bOldButton || aListValPos != aOldPos )
5421
0
        {
5422
            // paint area of new button
5423
0
            if ( comphelper::LibreOfficeKit::isActive() )
5424
0
            {
5425
0
                updateLOKValListButton( true, aListValPos );
5426
0
            }
5427
0
            else
5428
0
            {
5429
0
                Invalidate( PixelToLogic( GetListValButtonRect( aListValPos ) ) );
5430
0
            }
5431
0
        }
5432
0
    }
5433
0
    if ( !bOldButton )
5434
0
        return;
5435
5436
0
    if ( !bListValButton || aListValPos != aOldPos )
5437
0
    {
5438
        // paint area of old button
5439
0
        if ( comphelper::LibreOfficeKit::isActive() )
5440
0
        {
5441
0
            updateLOKValListButton( false, aOldPos );
5442
0
        }
5443
0
        else
5444
0
        {
5445
0
            Invalidate( PixelToLogic( GetListValButtonRect( aOldPos ) ) );
5446
0
        }
5447
0
    }
5448
0
}
5449
5450
void ScGridWindow::HideCursor()
5451
0
{
5452
0
    ++nCursorHideCount;
5453
0
}
5454
5455
void ScGridWindow::ShowCursor()
5456
0
{
5457
0
    --nCursorHideCount;
5458
0
}
5459
5460
void ScGridWindow::GetFocus()
5461
0
{
5462
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5463
0
    pViewShell->SetFormShellAtTop( false );     // focus in GridWindow -> FormShell no longer on top
5464
5465
0
    if (pViewShell->HasAccessibilityObjects())
5466
0
        pViewShell->BroadcastAccessibility(ScAccGridWinFocusGotHint(eWhich));
5467
5468
0
    if (!ScModule::get()->IsFormulaMode())
5469
0
    {
5470
0
        pViewShell->UpdateInputHandler();
5471
//      StopMarking();      // If Dialog (error), because then no ButtonUp
5472
                            // MO: only when not in RefInput mode
5473
                            //     -> GetFocus/MouseButtonDown order on Mac
5474
0
    }
5475
5476
0
    mrViewData.GetDocShell()->CheckConfigOptions();
5477
0
    Window::GetFocus();
5478
0
}
5479
5480
void ScGridWindow::LoseFocus()
5481
0
{
5482
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5483
5484
0
    if (pViewShell && pViewShell->HasAccessibilityObjects())
5485
0
        pViewShell->BroadcastAccessibility(ScAccGridWinFocusLostHint(eWhich));
5486
5487
0
    Window::LoseFocus();
5488
0
}
5489
5490
bool ScGridWindow::HitRangeFinder( const Point& rMouse, RfCorner& rCorner,
5491
                                sal_uInt16* pIndex, SCCOL* pAddX, SCROW* pAddY)
5492
0
{
5493
0
    bool bFound = false;
5494
0
    ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
5495
0
    if (pHdl)
5496
0
    {
5497
0
        ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
5498
0
        if ( pRangeFinder && !pRangeFinder->IsHidden() &&
5499
0
                pRangeFinder->GetDocName() == mrViewData.GetDocShell()->GetTitle() )
5500
0
        {
5501
0
            ScDocument& rDoc = mrViewData.GetDocument();
5502
0
            SCTAB nTab = mrViewData.CurrentTabForData();
5503
0
            bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
5504
0
            tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
5505
5506
0
            SCCOL nPosX;
5507
0
            SCROW nPosY;
5508
0
            mrViewData.GetPosFromPixel( rMouse.X(), rMouse.Y(), eWhich, nPosX, nPosY );
5509
            //  merged (single/Range) ???
5510
0
            ScAddress aAddr( nPosX, nPosY, nTab );
5511
5512
0
            Point aCellStart = mrViewData.GetScrPos( nPosX, nPosY, eWhich, true );
5513
0
            Point aCellEnd = aCellStart;
5514
0
            tools::Long nSizeXPix;
5515
0
            tools::Long nSizeYPix;
5516
0
            mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeXPix, nSizeYPix );
5517
5518
0
            aCellEnd.AdjustX(nSizeXPix * nLayoutSign );
5519
0
            aCellEnd.AdjustY(nSizeYPix );
5520
5521
0
            bool bCornerHorizontalRight;
5522
0
            bool bCornerHorizontalLeft;
5523
0
            if ( bLayoutRTL )
5524
0
            {
5525
0
                bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X()       && rMouse.X() <= aCellEnd.X() + 8 );
5526
0
                bCornerHorizontalLeft  = ( rMouse.X() >= aCellStart.X() - 8 && rMouse.X() <= aCellStart.X() );
5527
0
            }
5528
0
            else
5529
0
            {
5530
0
                bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() - 8 && rMouse.X() <= aCellEnd.X() );
5531
0
                bCornerHorizontalLeft  = ( rMouse.X() >= aCellStart.X()   && rMouse.X() <= aCellStart.X() + 8 );
5532
0
            }
5533
5534
0
            bool bCornerVerticalDown = rMouse.Y() >= aCellEnd.Y() - 8  && rMouse.Y() <= aCellEnd.Y();
5535
0
            bool bCornerVerticalUp   = rMouse.Y() >= aCellStart.Y()    && rMouse.Y() <= aCellStart.Y() + 8;
5536
5537
            //  corner is hit only if the mouse is within the cell
5538
0
            sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
5539
0
            for (sal_uInt16 i=nCount; i;)
5540
0
            {
5541
                //  search backwards so that the last repainted frame is found
5542
0
                --i;
5543
0
                ScRangeFindData& rData = pRangeFinder->GetObject(i);
5544
0
                if ( rData.aRef.Contains(aAddr) )
5545
0
                {
5546
0
                    if (pIndex)
5547
0
                        *pIndex = i;
5548
0
                    if (pAddX)
5549
0
                        *pAddX = nPosX - rData.aRef.aStart.Col();
5550
0
                    if (pAddY)
5551
0
                        *pAddY = nPosY - rData.aRef.aStart.Row();
5552
5553
0
                    bFound = true;
5554
5555
0
                    rCorner = NONE;
5556
5557
0
                    ScAddress aEnd = rData.aRef.aEnd;
5558
0
                    ScAddress aStart = rData.aRef.aStart;
5559
5560
0
                    if ( bCornerHorizontalLeft && bCornerVerticalUp &&
5561
0
                         aAddr == aStart)
5562
0
                    {
5563
0
                        rCorner = LEFT_UP;
5564
0
                    }
5565
0
                    else if (bCornerHorizontalRight && bCornerVerticalDown &&
5566
0
                             aAddr == aEnd)
5567
0
                    {
5568
0
                        rCorner = RIGHT_DOWN;
5569
0
                    }
5570
0
                    else if (bCornerHorizontalRight && bCornerVerticalUp &&
5571
0
                             aAddr == ScAddress(aEnd.Col(), aStart.Row(), aStart.Tab()))
5572
0
                    {
5573
0
                        rCorner = RIGHT_UP;
5574
0
                    }
5575
0
                    else if (bCornerHorizontalLeft && bCornerVerticalDown &&
5576
0
                             aAddr == ScAddress(aStart.Col(), aEnd.Row(), aStart.Tab()))
5577
0
                    {
5578
0
                        rCorner = LEFT_DOWN;
5579
0
                    }
5580
0
                    break;
5581
0
                }
5582
0
            }
5583
0
        }
5584
0
    }
5585
0
    return bFound;
5586
0
}
5587
5588
0
#define SCE_TOP     1
5589
0
#define SCE_BOTTOM  2
5590
0
#define SCE_LEFT    4
5591
0
#define SCE_RIGHT   8
5592
0
#define SCE_ALL     15
5593
5594
static void lcl_PaintOneRange( ScDocShell* pDocSh, const ScRange& rRange, sal_uInt16 nEdges )
5595
0
{
5596
    // the range is always properly oriented
5597
5598
0
    SCCOL nCol1 = rRange.aStart.Col();
5599
0
    SCROW nRow1 = rRange.aStart.Row();
5600
0
    SCTAB nTab1 = rRange.aStart.Tab();
5601
0
    SCCOL nCol2 = rRange.aEnd.Col();
5602
0
    SCROW nRow2 = rRange.aEnd.Row();
5603
0
    SCTAB nTab2 = rRange.aEnd.Tab();
5604
0
    bool bHiddenEdge = false;
5605
0
    SCROW nTmp;
5606
5607
0
    ScDocument& rDoc = pDocSh->GetDocument();
5608
0
    while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab1) )
5609
0
    {
5610
0
        --nCol1;
5611
0
        bHiddenEdge = true;
5612
0
    }
5613
0
    while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab1) )
5614
0
    {
5615
0
        ++nCol2;
5616
0
        bHiddenEdge = true;
5617
0
    }
5618
0
    nTmp = rDoc.FirstVisibleRow(0, nRow1, nTab1);
5619
0
    if (!rDoc.ValidRow(nTmp))
5620
0
        nTmp = 0;
5621
0
    if (nTmp < nRow1)
5622
0
    {
5623
0
        nRow1 = nTmp;
5624
0
        bHiddenEdge = true;
5625
0
    }
5626
0
    nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab1);
5627
0
    if (!rDoc.ValidRow(nTmp))
5628
0
        nTmp = rDoc.MaxRow();
5629
0
    if (nTmp > nRow2)
5630
0
    {
5631
0
        nRow2 = nTmp;
5632
0
        bHiddenEdge = true;
5633
0
    }
5634
5635
0
    if ( nCol2 > nCol1 + 1 && nRow2 > nRow1 + 1 && !bHiddenEdge )
5636
0
    {
5637
        // Only along the edges (The corners are hit twice)
5638
0
        if ( nEdges & SCE_TOP )
5639
0
            pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow1, nTab2, PaintPartFlags::Marks );
5640
0
        if ( nEdges & SCE_LEFT )
5641
0
            pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol1, nRow2, nTab2, PaintPartFlags::Marks );
5642
0
        if ( nEdges & SCE_RIGHT )
5643
0
            pDocSh->PostPaint( nCol2, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
5644
0
        if ( nEdges & SCE_BOTTOM )
5645
0
            pDocSh->PostPaint( nCol1, nRow2, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
5646
0
    }
5647
0
    else    // everything in one call
5648
0
        pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
5649
0
}
5650
5651
static void lcl_PaintRefChanged( ScDocShell* pDocSh, const ScRange& rOldUn, const ScRange& rNewUn )
5652
0
{
5653
    //  Repaint for the parts of the frame in old, which in are no more in New
5654
5655
0
    ScRange aOld = rOldUn;
5656
0
    ScRange aNew = rNewUn;
5657
0
    aOld.PutInOrder();
5658
0
    aNew.PutInOrder();
5659
5660
0
    if ( aOld.aStart == aOld.aEnd )                 //! Ignore sheet ?
5661
0
        pDocSh->GetDocument().ExtendMerge(aOld);
5662
0
    if ( aNew.aStart == aNew.aEnd )                 //! Ignore sheet ?
5663
0
        pDocSh->GetDocument().ExtendMerge(aNew);
5664
5665
0
    SCCOL nOldCol1 = aOld.aStart.Col();
5666
0
    SCROW nOldRow1 = aOld.aStart.Row();
5667
0
    SCCOL nOldCol2 = aOld.aEnd.Col();
5668
0
    SCROW nOldRow2 = aOld.aEnd.Row();
5669
0
    SCCOL nNewCol1 = aNew.aStart.Col();
5670
0
    SCROW nNewRow1 = aNew.aStart.Row();
5671
0
    SCCOL nNewCol2 = aNew.aEnd.Col();
5672
0
    SCROW nNewRow2 = aNew.aEnd.Row();
5673
0
    SCTAB nTab1 = aOld.aStart.Tab();        // sheet is not changed
5674
0
    SCTAB nTab2 = aOld.aEnd.Tab();
5675
5676
0
    if ( nNewRow2 < nOldRow1 || nNewRow1 > nOldRow2 ||
5677
0
         nNewCol2 < nOldCol1 || nNewCol1 > nOldCol2 ||
5678
0
         ( nNewCol1 != nOldCol1 && nNewRow1 != nOldRow1 &&
5679
0
           nNewCol2 != nOldCol2 && nNewRow2 != nOldRow2 ) )
5680
0
    {
5681
        // Completely removed or changed all sides
5682
        // (check <= instead of <  goes wrong for single rows/columns)
5683
5684
0
        lcl_PaintOneRange( pDocSh, aOld, SCE_ALL );
5685
0
    }
5686
0
    else        // Test all four corners separately
5687
0
    {
5688
        // upper part
5689
0
        if ( nNewRow1 < nOldRow1 )                  // only delete upper line
5690
0
            lcl_PaintOneRange( pDocSh, ScRange(
5691
0
                    nOldCol1, nOldRow1, nTab1, nOldCol2, nOldRow1, nTab2 ), SCE_ALL );
5692
0
        else if ( nNewRow1 > nOldRow1 )             // the upper part which is will be removed
5693
0
            lcl_PaintOneRange( pDocSh, ScRange(
5694
0
                    nOldCol1, nOldRow1, nTab1, nOldCol2, nNewRow1-1, nTab2 ),
5695
0
                    SCE_ALL &~ SCE_BOTTOM );
5696
5697
        //  bottom part
5698
0
        if ( nNewRow2 > nOldRow2 )                  // only delete bottom line
5699
0
            lcl_PaintOneRange( pDocSh, ScRange(
5700
0
                    nOldCol1, nOldRow2, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
5701
0
        else if ( nNewRow2 < nOldRow2 )             // the bottom part which is will be removed
5702
0
            lcl_PaintOneRange( pDocSh, ScRange(
5703
0
                    nOldCol1, nNewRow2+1, nTab1, nOldCol2, nOldRow2, nTab2 ),
5704
0
                    SCE_ALL &~ SCE_TOP );
5705
5706
        //  left part
5707
0
        if ( nNewCol1 < nOldCol1 )                  // only delete left line
5708
0
            lcl_PaintOneRange( pDocSh, ScRange(
5709
0
                    nOldCol1, nOldRow1, nTab1, nOldCol1, nOldRow2, nTab2 ), SCE_ALL );
5710
0
        else if ( nNewCol1 > nOldCol1 )             // the left part which is will be removed
5711
0
            lcl_PaintOneRange( pDocSh, ScRange(
5712
0
                    nOldCol1, nOldRow1, nTab1, nNewCol1-1, nOldRow2, nTab2 ),
5713
0
                    SCE_ALL &~ SCE_RIGHT );
5714
5715
        // right part
5716
0
        if ( nNewCol2 > nOldCol2 )                  // only delete right line
5717
0
            lcl_PaintOneRange( pDocSh, ScRange(
5718
0
                    nOldCol2, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
5719
0
        else if ( nNewCol2 < nOldCol2 )             // the right part which is will be removed
5720
0
            lcl_PaintOneRange( pDocSh, ScRange(
5721
0
                    nNewCol2+1, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ),
5722
0
                    SCE_ALL &~ SCE_LEFT );
5723
0
    }
5724
0
}
5725
5726
void ScGridWindow::RFMouseMove( const MouseEvent& rMEvt, bool bUp )
5727
0
{
5728
0
    ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
5729
0
    if (!pHdl)
5730
0
        return;
5731
0
    ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
5732
0
    if (!pRangeFinder || nRFIndex >= pRangeFinder->Count())
5733
0
        return;
5734
0
    ScRangeFindData& rData = pRangeFinder->GetObject( nRFIndex );
5735
5736
    // Mouse pointer
5737
5738
0
    if (bRFSize)
5739
0
        SetPointer( PointerStyle::Cross );
5740
0
    else
5741
0
        SetPointer( PointerStyle::Hand );
5742
5743
    //  Scrolling
5744
5745
0
    bool bTimer = false;
5746
0
    Point aPos = rMEvt.GetPosPixel();
5747
0
    SCCOL nDx = 0;
5748
0
    SCROW nDy = 0;
5749
0
    if ( aPos.X() < 0 ) nDx = -1;
5750
0
    if ( aPos.Y() < 0 ) nDy = -1;
5751
0
    Size aSize = GetOutputSizePixel();
5752
0
    if ( aPos.X() >= aSize.Width() )
5753
0
        nDx = 1;
5754
0
    if ( aPos.Y() >= aSize.Height() )
5755
0
        nDy = 1;
5756
0
    if ( nDx != 0 || nDy != 0 )
5757
0
    {
5758
0
        if ( nDx != 0) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
5759
0
        if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
5760
0
        bTimer = true;
5761
0
    }
5762
5763
    // Switching when fixating (so Scrolling works)
5764
5765
0
    if ( eWhich == mrViewData.GetActivePart() )     //??
5766
0
    {
5767
0
        if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX )
5768
0
            if ( nDx > 0 )
5769
0
            {
5770
0
                if ( eWhich == SC_SPLIT_TOPLEFT )
5771
0
                    mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
5772
0
                else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
5773
0
                    mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
5774
0
            }
5775
5776
0
        if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX )
5777
0
            if ( nDy > 0 )
5778
0
            {
5779
0
                if ( eWhich == SC_SPLIT_TOPLEFT )
5780
0
                    mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
5781
0
                else if ( eWhich == SC_SPLIT_TOPRIGHT )
5782
0
                    mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
5783
0
            }
5784
0
    }
5785
5786
    // Move
5787
5788
0
    SCCOL  nPosX;
5789
0
    SCROW  nPosY;
5790
0
    mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
5791
5792
0
    ScRange aOld = rData.aRef;
5793
0
    ScRange aNew = aOld;
5794
0
    if ( bRFSize )
5795
0
    {
5796
0
        switch (aRFSelectedCorned)
5797
0
        {
5798
0
            case LEFT_UP:
5799
0
                aNew.aStart.SetCol(nPosX);
5800
0
                aNew.aStart.SetRow(nPosY);
5801
0
                break;
5802
0
            case LEFT_DOWN:
5803
0
                aNew.aStart.SetCol(nPosX);
5804
0
                aNew.aEnd.SetRow(nPosY);
5805
0
                break;
5806
0
            case RIGHT_UP:
5807
0
                aNew.aEnd.SetCol(nPosX);
5808
0
                aNew.aStart.SetRow(nPosY);
5809
0
                break;
5810
0
            case RIGHT_DOWN:
5811
0
                aNew.aEnd.SetCol(nPosX);
5812
0
                aNew.aEnd.SetRow(nPosY);
5813
0
                break;
5814
0
            default:
5815
0
                break;
5816
0
        }
5817
0
    }
5818
0
    else
5819
0
    {
5820
0
        ScDocument& rDoc = mrViewData.GetDocument();
5821
0
        tools::Long nStartX = nPosX - nRFAddX;
5822
0
        if ( nStartX < 0 ) nStartX = 0;
5823
0
        tools::Long nStartY = nPosY - nRFAddY;
5824
0
        if ( nStartY < 0 ) nStartY = 0;
5825
0
        tools::Long nEndX = nStartX + aOld.aEnd.Col() - aOld.aStart.Col();
5826
0
        if ( nEndX > rDoc.MaxCol() )
5827
0
        {
5828
0
            nStartX -= ( nEndX - rDoc.MaxRow() );
5829
0
            nEndX = rDoc.MaxCol();
5830
0
        }
5831
0
        tools::Long nEndY = nStartY + aOld.aEnd.Row() - aOld.aStart.Row();
5832
0
        if ( nEndY > rDoc.MaxRow() )
5833
0
        {
5834
0
            nStartY -= ( nEndY - rDoc.MaxRow() );
5835
0
            nEndY = rDoc.MaxRow();
5836
0
        }
5837
5838
0
        aNew.aStart.SetCol(static_cast<SCCOL>(nStartX));
5839
0
        aNew.aStart.SetRow(static_cast<SCROW>(nStartY));
5840
0
        aNew.aEnd.SetCol(static_cast<SCCOL>(nEndX));
5841
0
        aNew.aEnd.SetRow(static_cast<SCROW>(nEndY));
5842
0
    }
5843
5844
0
    if ( bUp )
5845
0
        aNew.PutInOrder();             // For ButtonUp again in the proper order
5846
5847
0
    if ( aNew != aOld )
5848
0
    {
5849
0
        pHdl->UpdateRange( nRFIndex, aNew );
5850
5851
0
        ScDocShell* pDocSh = mrViewData.GetDocShell();
5852
5853
0
        pHdl->UpdateLokReferenceMarks();
5854
5855
        // only redrawing what has been changed...
5856
0
        lcl_PaintRefChanged( pDocSh, aOld, aNew );
5857
5858
        // only redraw new frame (synchronously)
5859
0
        pDocSh->Broadcast( ScIndexHint( SfxHintId::ScShowRangeFinder, nRFIndex ) );
5860
5861
0
        PaintImmediately();   // what you move, will be seen immediately
5862
0
    }
5863
5864
    //  Timer for Scrolling
5865
5866
0
    if (bTimer)
5867
0
        mrViewData.GetView()->SetTimer( this, rMEvt );          // repeat event
5868
0
    else
5869
0
        mrViewData.GetView()->ResetTimer();
5870
0
}
5871
5872
namespace {
5873
5874
SvxAdjust toSvxAdjust( const ScPatternAttr& rPat )
5875
0
{
5876
0
    SvxCellHorJustify eHorJust =
5877
0
            rPat.GetItem(ATTR_HOR_JUSTIFY).GetValue();
5878
5879
0
    SvxAdjust eSvxAdjust = SvxAdjust::Left;
5880
0
    switch (eHorJust)
5881
0
    {
5882
0
        case SvxCellHorJustify::Left:
5883
0
        case SvxCellHorJustify::Repeat:            // not implemented
5884
0
        case SvxCellHorJustify::Standard:          // always Text if an EditCell type
5885
0
                eSvxAdjust = SvxAdjust::Left;
5886
0
                break;
5887
0
        case SvxCellHorJustify::Right:
5888
0
                eSvxAdjust = SvxAdjust::Right;
5889
0
                break;
5890
0
        case SvxCellHorJustify::Center:
5891
0
                eSvxAdjust = SvxAdjust::Center;
5892
0
                break;
5893
0
        case SvxCellHorJustify::Block:
5894
0
                eSvxAdjust = SvxAdjust::Block;
5895
0
                break;
5896
0
    }
5897
5898
0
    return eSvxAdjust;
5899
0
}
5900
5901
std::shared_ptr<ScFieldEditEngine> createEditEngine( ScDocShell* pDocSh, const ScPatternAttr& rPat )
5902
0
{
5903
0
    ScDocument& rDoc = pDocSh->GetDocument();
5904
5905
0
    auto pEngine = std::make_shared<ScFieldEditEngine>(&rDoc, rDoc.GetEditEnginePool());
5906
0
    ScSizeDeviceProvider aProv(*pDocSh);
5907
0
    pEngine->SetRefDevice(aProv.GetDevice());
5908
0
    pEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
5909
0
    SfxItemSet aDefault(pEngine->GetEmptyItemSet());
5910
0
    rPat.FillEditItemSet(&aDefault);
5911
0
    aDefault.Put(SvxAdjustItem(toSvxAdjust(rPat), EE_PARA_JUST));
5912
0
    pEngine->SetDefaults(std::move(aDefault));
5913
5914
0
    return pEngine;
5915
0
}
5916
5917
bool extractURLInfo( const SvxFieldItem* pFieldItem, OUString* pName, OUString* pUrl, OUString* pTarget )
5918
0
{
5919
0
    if (!pFieldItem)
5920
0
        return false;
5921
5922
0
    const SvxFieldData* pField = pFieldItem->GetField();
5923
0
    if (pField->GetClassId() != text::textfield::Type::URL)
5924
0
        return false;
5925
5926
0
    const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
5927
5928
0
    if (pName)
5929
0
        *pName = pURLField->GetRepresentation();
5930
0
    if (pUrl)
5931
0
        *pUrl = pURLField->GetURL();
5932
0
    if (pTarget)
5933
0
        *pTarget = pURLField->GetTargetFrame();
5934
5935
0
    return true;
5936
0
}
5937
5938
}
5939
5940
static void lcl_SetEngineTextKeepingDefaults(const std::shared_ptr<ScFieldEditEngine>& pEngine,
5941
                                             ScDocument& rDoc, ScRefCellValue& rCell, const OUString& rURL)
5942
0
{
5943
0
    std::optional<EditTextObject> oTextObj;
5944
0
    if (rCell.getType() == CELLTYPE_EDIT)
5945
0
    {
5946
0
        if (rCell.getEditText())
5947
0
            pEngine->SetTextCurrentDefaults(*rCell.getEditText());
5948
0
    }
5949
0
    else  // Not an Edit cell and is a formula cell with 'Hyperlink'
5950
          // function if we have no URL, otherwise it could be a formula
5951
          // cell ( or other type ? ) with a hyperlink associated with it.
5952
0
    {
5953
0
        if (rURL.isEmpty())
5954
0
            oTextObj.emplace(rCell.getFormula()->CreateURLObject());
5955
0
        else
5956
0
        {
5957
0
            OUString aRepres = rURL;
5958
5959
            // TODO: text content of formatted numbers can be different
5960
0
            if (rCell.hasNumeric())
5961
0
                aRepres = OUString::number(rCell.getValue());
5962
0
            else if (rCell.getType() == CELLTYPE_FORMULA)
5963
0
                aRepres = rCell.getFormula()->GetString().getString();
5964
5965
0
            oTextObj.emplace(ScEditUtil::CreateURLObjectFromURL(rDoc, rURL, aRepres));
5966
0
        }
5967
5968
0
        if (oTextObj)
5969
0
            pEngine->SetTextCurrentDefaults(*oTextObj);
5970
0
    }
5971
0
}
5972
5973
static std::vector<std::unique_ptr<SvxFieldItem>> lcl_GetEditEngineFields(const ScFieldEditEngine& rEditEngine)
5974
0
{
5975
0
    std::vector<std::unique_ptr<SvxFieldItem>> vFieldVect;
5976
5977
0
    sal_Int32 nPara = rEditEngine.GetParagraphCount();
5978
0
    for (sal_Int32 nCurrPara = 0; nCurrPara < nPara; ++nCurrPara)
5979
0
    {
5980
0
        for (EFieldInfo& rFieldInfo : rEditEngine.GetFieldInfo(nCurrPara))
5981
0
        {
5982
0
            vFieldVect.push_back(std::move(rFieldInfo.pFieldItem));
5983
0
        }
5984
0
    }
5985
0
    return vFieldVect;
5986
0
}
5987
5988
5989
std::vector<UrlData> ScGridWindow::GetEditUrls(const ScAddress& rSelectedCell)
5990
0
{
5991
0
    ScDocShell* pDocSh = mrViewData.GetDocShell();
5992
0
    ScDocument& rDoc = pDocSh->GetDocument();
5993
5994
0
    SCCOL nPosX = rSelectedCell.Col();
5995
0
    SCROW nPosY = rSelectedCell.Row();
5996
0
    SCTAB nTab  = rSelectedCell.Tab();
5997
5998
0
    OUString sURL;
5999
0
    ScRefCellValue aCell;
6000
0
    std::vector<UrlData> vUrls;
6001
0
    if (!lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
6002
0
        return vUrls;
6003
6004
0
    if (nPosX != rSelectedCell.Col())
6005
0
        return vUrls;
6006
6007
0
    const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
6008
6009
0
    std::shared_ptr<ScFieldEditEngine> pEngine = createEditEngine(pDocSh, *pPattern);
6010
6011
0
    lcl_SetEngineTextKeepingDefaults(pEngine, rDoc, aCell, sURL);
6012
6013
0
    std::vector<std::unique_ptr<SvxFieldItem>> vFieldItems = lcl_GetEditEngineFields(*pEngine);
6014
0
    for (auto& pFieldItem : vFieldItems)
6015
0
    {
6016
0
        UrlData aData;
6017
0
        bool bIsUrl = extractURLInfo(pFieldItem.get(), &aData.aName, &aData.aUrl, &aData.aTarget);
6018
0
        if (bIsUrl && !aData.aUrl.isEmpty())
6019
0
            vUrls.push_back(aData);
6020
0
    }
6021
0
    return vUrls;
6022
0
}
6023
6024
bool ScGridWindow::GetEditUrl(const Point& rPos, OUString* pName, OUString* pUrl, OUString* pTarget,
6025
                              SCCOL* pnCol)
6026
0
{
6027
0
    ScTabViewShell* pViewSh = mrViewData.GetViewShell();
6028
0
    ScInputHandler* pInputHdl = nullptr;
6029
0
    if (pViewSh)
6030
0
        pInputHdl = pViewSh->GetInputHandler();
6031
0
    EditView* pView = (pInputHdl && pInputHdl->IsInputMode()) ? pInputHdl->GetTableView() : nullptr;
6032
0
    if (pView)
6033
0
        return extractURLInfo(pView->GetFieldUnderMousePointer(), pName, pUrl, pTarget);
6034
6035
    //! Pass on nPosX/Y?
6036
0
    SCCOL nPosX;
6037
0
    SCROW nPosY;
6038
0
    mrViewData.GetPosFromPixel( rPos.X(), rPos.Y(), eWhich, nPosX, nPosY );
6039
6040
0
    SCTAB nTab = mrViewData.CurrentTabForData();
6041
0
    ScDocShell* pDocSh = mrViewData.GetDocShell();
6042
0
    ScDocument& rDoc = pDocSh->GetDocument();
6043
0
    OUString sURL;
6044
0
    ScRefCellValue aCell;
6045
0
    bool bFound = lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL);
6046
0
    if( !bFound )
6047
0
        return false;
6048
0
    if (pnCol)
6049
0
        *pnCol = nPosX;
6050
6051
0
    const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
6052
    // bForceToTop = sal_False, use the cell's real position
6053
0
    tools::Rectangle aEditRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, false );
6054
0
    if (rPos.Y() < aEditRect.Top())
6055
0
        return false;
6056
6057
        // vertical can not (yet) be clicked:
6058
6059
0
    if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
6060
0
        return false;
6061
6062
0
    bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
6063
0
                    (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block);
6064
0
    SvxCellHorJustify eHorJust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
6065
6066
        //  EditEngine
6067
6068
0
    std::shared_ptr<ScFieldEditEngine> pEngine = createEditEngine(pDocSh, *pPattern);
6069
6070
0
    MapMode aEditMode = mrViewData.GetLogicMode(eWhich);            // without draw scaling
6071
0
    tools::Rectangle aLogicEdit = PixelToLogic( aEditRect, aEditMode );
6072
0
    tools::Long nThisColLogic = aLogicEdit.Right() - aLogicEdit.Left() + 1;
6073
0
    Size aPaperSize( 1000000, 1000000 );
6074
0
    if (aCell.getType() == CELLTYPE_FORMULA)
6075
0
    {
6076
0
        tools::Long nSizeX  = 0;
6077
0
        tools::Long nSizeY  = 0;
6078
0
        mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeX, nSizeY );
6079
0
        aPaperSize = Size(nSizeX, nSizeY );
6080
0
        aPaperSize = PixelToLogic(aPaperSize);
6081
0
    }
6082
6083
0
    if (bBreak)
6084
0
        aPaperSize.setWidth( nThisColLogic );
6085
0
    pEngine->SetPaperSize( aPaperSize );
6086
6087
0
    lcl_SetEngineTextKeepingDefaults(pEngine, rDoc, aCell, sURL);
6088
6089
0
    tools::Long nStartX = aLogicEdit.Left();
6090
6091
0
    tools::Long nTextWidth = pEngine->CalcTextWidth();
6092
0
    tools::Long nTextHeight = pEngine->GetTextHeight();
6093
0
    if ( nTextWidth < nThisColLogic )
6094
0
    {
6095
0
        if (eHorJust == SvxCellHorJustify::Right)
6096
0
            nStartX += nThisColLogic - nTextWidth;
6097
0
        else if (eHorJust == SvxCellHorJustify::Center)
6098
0
            nStartX += (nThisColLogic - nTextWidth) / 2;
6099
0
    }
6100
6101
0
    aLogicEdit.SetLeft( nStartX );
6102
0
    if (!bBreak)
6103
0
        aLogicEdit.SetRight( nStartX + nTextWidth );
6104
6105
    // There is one glitch when dealing with a hyperlink cell and
6106
    // the cell content is NUMERIC. This defaults to right aligned and
6107
    // we need to adjust accordingly.
6108
0
    if (aCell.hasNumeric() && eHorJust == SvxCellHorJustify::Standard)
6109
0
    {
6110
0
        aLogicEdit.SetRight( aLogicEdit.Left() + nThisColLogic - 1 );
6111
0
        aLogicEdit.SetLeft(  aLogicEdit.Right() - nTextWidth );
6112
0
    }
6113
0
    aLogicEdit.SetBottom( aLogicEdit.Top() + nTextHeight );
6114
6115
0
    Point aLogicClick = PixelToLogic(rPos,aEditMode);
6116
0
    if ( aLogicEdit.Contains(aLogicClick) )
6117
0
    {
6118
0
        EditView aTempView(*pEngine, this);
6119
0
        aTempView.SetOutputArea( aLogicEdit );
6120
6121
0
        bool bRet;
6122
0
        if (comphelper::LibreOfficeKit::isActive())
6123
0
        {
6124
0
            bRet = extractURLInfo(aTempView.GetField(aLogicClick), pName, pUrl, pTarget);
6125
0
        }
6126
0
        else
6127
0
        {
6128
0
            MapMode aOld = GetMapMode();
6129
0
            SetMapMode(aEditMode);                  // no return anymore
6130
0
            bRet = extractURLInfo(aTempView.GetFieldUnderMousePointer(), pName, pUrl, pTarget);
6131
0
            SetMapMode(aOld);
6132
0
        }
6133
0
        return bRet;
6134
0
    }
6135
0
    return false;
6136
0
}
6137
6138
bool ScGridWindow::HasScenarioButton( const Point& rPosPixel, ScRange& rScenRange )
6139
0
{
6140
0
    ScDocument& rDoc = mrViewData.GetDocument();
6141
0
    SCTAB nTab = mrViewData.CurrentTabForData();
6142
0
    SCTAB nTabCount = rDoc.GetTableCount();
6143
0
    if ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) )
6144
0
    {
6145
0
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6146
6147
0
        Size aButSize = mrViewData.GetScenButSize();
6148
0
        tools::Long nBWidth  = aButSize.Width();
6149
0
        if (!nBWidth)
6150
0
            return false;                   // No Button drawn yet -> there is none
6151
0
        tools::Long nBHeight = aButSize.Height();
6152
0
        tools::Long nHSpace  = static_cast<tools::Long>( SC_SCENARIO_HSPACE * mrViewData.GetPPTX() );
6153
6154
        //! cache the Ranges in Table!!!!
6155
6156
0
        ScMarkData aMarks(rDoc.GetSheetLimits());
6157
0
        for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
6158
0
            rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
6159
0
        ScRangeList aRanges;
6160
0
        aMarks.FillRangeListWithMarks( &aRanges, false );
6161
6162
0
        size_t nRangeCount = aRanges.size();
6163
0
        for (size_t j=0;  j< nRangeCount; ++j)
6164
0
        {
6165
0
            ScRange aRange = aRanges[j];
6166
            // Always extend scenario frame to merged cells where no new non-covered cells
6167
            // are framed
6168
0
            rDoc.ExtendTotalMerge( aRange );
6169
6170
0
            bool bTextBelow = ( aRange.aStart.Row() == 0 );
6171
6172
0
            Point aButtonPos;
6173
0
            if ( bTextBelow )
6174
0
            {
6175
0
                aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1,
6176
0
                                                    eWhich, true );
6177
0
            }
6178
0
            else
6179
0
            {
6180
0
                aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aStart.Row(),
6181
0
                                                    eWhich, true );
6182
0
                aButtonPos.AdjustY( -nBHeight );
6183
0
            }
6184
0
            if ( bLayoutRTL )
6185
0
                aButtonPos.AdjustX( -(nHSpace - 1) );
6186
0
            else
6187
0
                aButtonPos.AdjustX( -(nBWidth - nHSpace) );    // same for top or bottom
6188
6189
0
            tools::Rectangle aButRect( aButtonPos, Size(nBWidth,nBHeight) );
6190
0
            if ( aButRect.Contains( rPosPixel ) )
6191
0
            {
6192
0
                rScenRange = aRange;
6193
0
                return true;
6194
0
            }
6195
0
        }
6196
0
    }
6197
6198
0
    return false;
6199
0
}
6200
6201
void ScGridWindow::DrawLayerCreated()
6202
0
{
6203
0
    SetMapMode( GetDrawMapMode() );
6204
6205
    // initially create overlay objects
6206
0
    ImpCreateOverlayObjects();
6207
0
}
6208
6209
void ScGridWindow::SetAutoSpellContext( const std::shared_ptr<sc::SpellCheckContext> &ctx )
6210
0
{
6211
0
    mpSpellCheckCxt = ctx;
6212
0
}
6213
6214
void ScGridWindow::ResetAutoSpell()
6215
0
{
6216
0
    if (mpSpellCheckCxt)
6217
0
        mpSpellCheckCxt->reset();
6218
0
}
6219
6220
void ScGridWindow::ResetAutoSpellForContentChange()
6221
0
{
6222
0
    if (mpSpellCheckCxt)
6223
0
        mpSpellCheckCxt->resetForContentChange();
6224
0
}
6225
6226
void ScGridWindow::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const sc::MisspellRangeResult& rRangeResult )
6227
0
{
6228
0
    if (!mpSpellCheckCxt)
6229
0
        return;
6230
6231
0
    mpSpellCheckCxt->setMisspellRanges(nPosX, nPosY, rRangeResult);
6232
0
}
6233
6234
sc::MisspellRangeResult ScGridWindow::GetAutoSpellData( SCCOL nPosX, SCROW nPosY )
6235
0
{
6236
0
    if (!mpSpellCheckCxt)
6237
0
        return {};
6238
6239
0
    if (!maVisibleRange.isInside(nPosX, nPosY))
6240
0
        return {};
6241
6242
0
    return mpSpellCheckCxt->getMisspellRanges(nPosX, nPosY);
6243
0
}
6244
6245
bool ScGridWindow::InsideVisibleRange( SCCOL nPosX, SCROW nPosY )
6246
0
{
6247
0
    return maVisibleRange.isInside(nPosX, nPosY);
6248
0
}
6249
6250
OString ScGridWindow::getCellCursor() const
6251
0
{
6252
    // GridWindow stores a shown cell cursor in mpOOCursors, hence
6253
    // we can use that to determine whether we would want to be showing
6254
    // one (client-side) for tiled rendering too.
6255
0
    if (!mpOOCursors)
6256
0
        return "EMPTY"_ostr;
6257
6258
0
    if (comphelper::LibreOfficeKit::isCompatFlagSet(
6259
0
            comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6260
0
        return mrViewData.describeCellCursorInPrintTwips();
6261
6262
0
    return mrViewData.describeCellCursor();
6263
0
}
6264
6265
void ScGridWindow::notifyKitCellCursor() const
6266
0
{
6267
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6268
6269
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, getCellCursor());
6270
0
    if (bListValButton && aListValPos == mrViewData.GetCurPos())
6271
0
        updateLOKValListButton(true, aListValPos);
6272
0
    std::vector<tools::Rectangle> aRects;
6273
0
    GetSelectionRects(aRects);
6274
0
    if (aRects.empty() || !mrViewData.IsActive())
6275
0
    {
6276
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
6277
0
        SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
6278
0
    }
6279
0
}
6280
6281
void ScGridWindow::notifyKitCellViewCursor(const SfxViewShell* pForShell) const
6282
0
{
6283
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6284
6285
0
    if (pViewShell->GetDocId() != pForShell->GetDocId())
6286
0
        return;
6287
6288
0
    OString aCursor("EMPTY"_ostr);
6289
0
    if (mpOOCursors) // cf. getCellCursor above
6290
0
    {
6291
0
        auto pForTabView = dynamic_cast<const ScTabViewShell *>(pForShell);
6292
0
        if (!pForTabView)
6293
0
            return;
6294
6295
0
        if (comphelper::LibreOfficeKit::isCompatFlagSet(
6296
0
            comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6297
0
            aCursor = mrViewData.describeCellCursorInPrintTwips();
6298
0
        else
6299
0
            aCursor = pForTabView->GetViewData().describeCellCursorAt(
6300
0
                mrViewData.GetCurX(), mrViewData.GetCurY()); // our position.
6301
0
    }
6302
0
    SfxLokHelper::notifyOtherView(*pViewShell, pForShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
6303
0
}
6304
6305
// Send our cursor details to a view described by @pForShell, or all views
6306
// if @pForShell is null. In each case send the current view a cell-cursor
6307
// event, and others a cell_view_cursor event.
6308
//
6309
// NB. we need to re-construct the cursor details for each other view in their
6310
// own zoomed co-ordinate system (but not in scPrintTwipsMsgs mode).
6311
void ScGridWindow::updateKitCellCursor(const SfxViewShell* pForShell) const
6312
0
{
6313
0
    if (comphelper::LibreOfficeKit::isCompatFlagSet(
6314
0
            comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6315
0
    {
6316
0
        ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6317
        // Generate the cursor info string just once and directly send to all.
6318
        // Calling notifyKitCellViewCursor() would regenerate the
6319
        // cursor-string unnecessarily.
6320
0
        OString aCursor = getCellCursor();
6321
6322
0
        if (pForShell)
6323
0
        {
6324
0
            SfxLokHelper::notifyOtherView(*pViewShell, pForShell,
6325
0
                    LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
6326
0
        }
6327
0
        else
6328
0
        {
6329
0
            notifyKitCellCursor();
6330
0
            SfxLokHelper::notifyOtherViews(pViewShell,
6331
0
                    LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
6332
0
        }
6333
6334
0
        return;
6335
0
    }
6336
6337
0
    if (!pForShell)
6338
0
    {
6339
0
        for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6340
0
             it = SfxViewShell::GetNext(*it))
6341
0
            updateKitCellCursor(it);
6342
0
        return;
6343
0
    }
6344
6345
0
    if (pForShell == mrViewData.GetViewShell())
6346
0
        notifyKitCellCursor();
6347
0
    else
6348
0
        notifyKitCellViewCursor(pForShell);
6349
0
}
6350
6351
void ScGridWindow::updateKitOtherCursors() const
6352
0
{
6353
0
    for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6354
0
         it = SfxViewShell::GetNext(*it))
6355
0
    {
6356
0
        auto pOther = dynamic_cast<const ScTabViewShell *>(it);
6357
0
        if (!pOther)
6358
0
            continue;
6359
0
        const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
6360
0
        assert(pGrid);
6361
0
        if (pGrid == this)
6362
0
            notifyKitCellCursor();
6363
0
        else
6364
0
            pGrid->notifyKitCellViewCursor(mrViewData.GetViewShell());
6365
0
    }
6366
0
}
6367
6368
void ScGridWindow::CursorChanged()
6369
0
{
6370
    // here the created OverlayObjects may be transformed in later versions. For
6371
    // now, just re-create them
6372
6373
0
    UpdateCursorOverlay();
6374
0
    UpdateAutoFillOverlay();
6375
0
    UpdateSparklineGroupOverlay();
6376
0
    UpdateDatabaseOverlay();
6377
0
}
6378
6379
void ScGridWindow::ImpCreateOverlayObjects()
6380
0
{
6381
0
    UpdateHighlightOverlay();
6382
0
    UpdateSelectionOverlay();
6383
0
    UpdateCursorOverlay();
6384
0
    UpdateAutoFillOverlay();
6385
0
    UpdateCopySourceOverlay();
6386
0
    UpdateDragRectOverlay();
6387
0
    UpdateHeaderOverlay();
6388
0
    UpdateShrinkOverlay();
6389
0
    UpdateSparklineGroupOverlay();
6390
0
    UpdateDatabaseOverlay();
6391
0
}
6392
6393
void ScGridWindow::ImpDestroyOverlayObjects()
6394
0
{
6395
0
    DeleteCursorOverlay();
6396
0
    DeleteCopySourceOverlay();
6397
0
    DeleteSelectionOverlay();
6398
0
    mpOOHighlight.reset();          // DeleteHighlightOverlay
6399
0
    DeleteAutoFillOverlay();
6400
0
    DeleteDragRectOverlay();
6401
0
    DeleteHeaderOverlay();
6402
0
    DeleteShrinkOverlay();
6403
0
    DeleteSparklineGroupOverlay();
6404
0
    DeleteDatabaseOverlay();
6405
0
}
6406
6407
void ScGridWindow::UpdateAllOverlays()
6408
0
{
6409
    // delete and re-allocate all overlay objects
6410
6411
0
    ImpDestroyOverlayObjects();
6412
0
    ImpCreateOverlayObjects();
6413
0
}
6414
6415
void ScGridWindow::DeleteCursorOverlay()
6416
0
{
6417
0
    if (comphelper::LibreOfficeKit::isActive() && mrViewData.HasEditView(eWhich))
6418
0
        return;
6419
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6420
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, "EMPTY"_ostr);
6421
0
    SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
6422
0
    mpOOCursors.reset();
6423
0
}
6424
6425
void ScGridWindow::DeleteCopySourceOverlay()
6426
0
{
6427
0
    mpOOSelectionBorder.reset();
6428
0
}
6429
6430
void ScGridWindow::UpdateCopySourceOverlay()
6431
0
{
6432
0
    const MapMode aDrawMode = GetDrawMapMode();
6433
0
    const MapMode aOldMode = GetMapMode();
6434
0
    comphelper::ScopeGuard aMapModeGuard(
6435
0
        [&aOldMode, &aDrawMode, this] {
6436
0
            if (aOldMode != aDrawMode)
6437
0
                SetMapMode(aOldMode);
6438
0
        }
6439
0
    );
6440
0
    if (aOldMode != aDrawMode)
6441
0
        SetMapMode(aDrawMode);
6442
6443
0
    DeleteCopySourceOverlay();
6444
6445
0
    if (comphelper::LibreOfficeKit::isActive())
6446
0
        return;
6447
0
    if (!mrViewData.ShowPasteSource())
6448
0
        return;
6449
0
    if (!ScModule::get()->GetInputOptions().GetEnterPasteMode())
6450
0
        return;
6451
0
    rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6452
0
    if (!xOverlayManager.is())
6453
0
        return;
6454
0
    const ScTransferObj* pTransObj = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(mrViewData.GetActiveWin()));
6455
0
    if (!pTransObj)
6456
0
        return;
6457
0
    ScDocument* pClipDoc = pTransObj->GetDocument();
6458
0
    if (!pClipDoc)
6459
0
        return;
6460
6461
0
    SCTAB nCurTab = mrViewData.GetCurPos().Tab();
6462
6463
0
    ScClipParam& rClipParam = pClipDoc->GetClipParam();
6464
0
    mpOOSelectionBorder.reset(new sdr::overlay::OverlayObjectList);
6465
0
    for ( size_t i = 0; i < rClipParam.maRanges.size(); ++i )
6466
0
    {
6467
0
        ScRange const & r = rClipParam.maRanges[i];
6468
0
        if (r.aStart.Tab() != nCurTab)
6469
0
            continue;
6470
6471
0
        SCCOL nClipStartX = r.aStart.Col();
6472
0
        SCROW nClipStartY = r.aStart.Row();
6473
0
        SCCOL nClipEndX   = r.aEnd.Col();
6474
0
        SCROW nClipEndY   = r.aEnd.Row();
6475
6476
0
        Point aClipStartScrPos = mrViewData.GetScrPos( nClipStartX, nClipStartY, eWhich );
6477
0
        Point aClipEndScrPos   = mrViewData.GetScrPos( nClipEndX + 1, nClipEndY + 1, eWhich );
6478
0
        aClipStartScrPos -= Point(1, 1);
6479
0
        tools::Long nSizeXPix = aClipEndScrPos.X() - aClipStartScrPos.X();
6480
0
        tools::Long nSizeYPix = aClipEndScrPos.Y() - aClipStartScrPos.Y();
6481
6482
0
        tools::Rectangle aRect( aClipStartScrPos, Size(nSizeXPix, nSizeYPix) );
6483
6484
0
        Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
6485
6486
0
        tools::Rectangle aLogic = PixelToLogic(aRect, aDrawMode);
6487
0
        ::basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aLogic);
6488
0
        std::unique_ptr<ScOverlayDashedBorder> pDashedBorder(new ScOverlayDashedBorder(aRange, aHighlight));
6489
0
        xOverlayManager->add(*pDashedBorder);
6490
0
        mpOOSelectionBorder->append(std::move(pDashedBorder));
6491
0
    }
6492
0
}
6493
6494
static std::vector<tools::Rectangle> convertPixelToLogical(
6495
    const ScViewData& rViewData,
6496
    const std::vector<tools::Rectangle>& rRectangles,
6497
    tools::Rectangle &rBoundingBox)
6498
0
{
6499
0
    std::vector<tools::Rectangle> aLogicRects;
6500
6501
0
    double nPPTX = rViewData.GetPPTX();
6502
0
    double nPPTY = rViewData.GetPPTY();
6503
6504
0
    for (const auto& rRectangle : rRectangles)
6505
0
    {
6506
        // We explicitly create a copy, since we need to expand
6507
        // the rectangle before coordinate conversion
6508
0
        tools::Rectangle aRectangle(rRectangle);
6509
0
        aRectangle.AdjustRight(1 );
6510
0
        aRectangle.AdjustBottom(1 );
6511
6512
0
        tools::Rectangle aRect(aRectangle.Left() / nPPTX, aRectangle.Top() / nPPTY,
6513
0
                aRectangle.Right() / nPPTX, aRectangle.Bottom() / nPPTY);
6514
6515
0
        rBoundingBox.Union(aRect);
6516
0
        aLogicRects.push_back(aRect);
6517
0
    }
6518
0
    return aLogicRects;
6519
0
}
6520
6521
static OString rectanglesToString(const std::vector<tools::Rectangle> &rLogicRects)
6522
0
{
6523
0
    bool bFirst = true;
6524
0
    OStringBuffer aRects;
6525
0
    for (const auto &rRect : rLogicRects)
6526
0
    {
6527
0
        if (!bFirst)
6528
0
            aRects.append("; ");
6529
0
        bFirst = false;
6530
0
        aRects.append(rRect.toString());
6531
0
    }
6532
0
    return aRects.makeStringAndClear();
6533
0
}
6534
6535
/**
6536
 * Turn the selection ranges rRectangles into the LibreOfficeKit selection, and send to other views.
6537
 *
6538
 * @param pLogicRects - if set then don't invoke the callback, just collect the rectangles in the pointed vector.
6539
 */
6540
void ScGridWindow::UpdateKitSelection(const std::vector<tools::Rectangle>& rRectangles, std::vector<tools::Rectangle>* pLogicRects)
6541
0
{
6542
0
    if (!comphelper::LibreOfficeKit::isActive())
6543
0
        return;
6544
6545
    // If this is true, rRectangles should already in print twips.
6546
    // If false, rRectangles are in pixels.
6547
0
    bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
6548
0
            comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
6549
6550
0
    tools::Rectangle aBoundingBox;
6551
0
    std::vector<tools::Rectangle> aConvertedRects;
6552
6553
0
    if (bInPrintTwips)
6554
0
        std::for_each(rRectangles.begin(), rRectangles.end(),
6555
0
                      [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
6556
0
    else
6557
0
        aConvertedRects = convertPixelToLogical(mrViewData, rRectangles, aBoundingBox);
6558
6559
0
    const std::vector<tools::Rectangle>& rLogicRects = bInPrintTwips ? rRectangles : aConvertedRects;
6560
0
    if (pLogicRects)
6561
0
    {
6562
0
        *pLogicRects = rLogicRects;
6563
0
        return;
6564
0
    }
6565
6566
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6567
0
    pViewShell->UpdateInputHandler();
6568
0
    OString sBoundingBoxString = "EMPTY"_ostr;
6569
0
    if (!aBoundingBox.IsEmpty())
6570
0
        sBoundingBoxString = aBoundingBox.toString();
6571
0
    OString aRectListString = rectanglesToString(rLogicRects);
6572
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
6573
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectListString);
6574
6575
0
    if (bInPrintTwips)
6576
0
    {
6577
0
        SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
6578
0
                                       "selection", aRectListString);
6579
0
        return;
6580
0
    }
6581
6582
0
    for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6583
0
         it = SfxViewShell::GetNext(*it))
6584
0
    {
6585
0
        if (it == pViewShell)
6586
0
            continue;
6587
0
        auto pOther = dynamic_cast<const ScTabViewShell *>(it);
6588
0
        if (!pOther)
6589
0
            return;
6590
6591
0
        const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
6592
0
        assert(pGrid);
6593
6594
        // Fetch pixels & convert for each view separately.
6595
0
        tools::Rectangle aDummyBBox;
6596
0
        std::vector<tools::Rectangle> aPixelRects;
6597
0
        pGrid->GetPixelRectsFor(mrViewData.GetMarkData() /* ours */, aPixelRects);
6598
0
        auto aOtherLogicRects = convertPixelToLogical(pOther->GetViewData(), aPixelRects, aDummyBBox);
6599
0
        SfxLokHelper::notifyOtherView(*pViewShell, pOther, LOK_CALLBACK_TEXT_VIEW_SELECTION,
6600
0
                                      "selection", rectanglesToString(aOtherLogicRects));
6601
0
    }
6602
0
}
6603
6604
/**
6605
 * Fetch the selection ranges for other views into the LibreOfficeKit selection,
6606
 * map them into our view co-ordinates and send to our view.
6607
 */
6608
void ScGridWindow::updateOtherKitSelections() const
6609
0
{
6610
0
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6611
0
    bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
6612
0
            comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
6613
6614
0
    for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6615
0
         it = SfxViewShell::GetNext(*it))
6616
0
    {
6617
0
        auto pOther = dynamic_cast<const ScTabViewShell *>(it);
6618
0
        if (!pOther)
6619
0
            continue;
6620
6621
        // Fetch pixels & convert for each view separately.
6622
0
        tools::Rectangle aBoundingBox;
6623
0
        std::vector<tools::Rectangle> aRects;
6624
0
        OString aRectsString;
6625
0
        GetRectsAnyFor(pOther->GetViewData().GetMarkData() /* theirs */, aRects, bInPrintTwips);
6626
0
        if (bInPrintTwips)
6627
0
        {
6628
0
            std::for_each(aRects.begin(), aRects.end(),
6629
0
                          [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
6630
0
            aRectsString = rectanglesToString(aRects);
6631
0
        }
6632
0
        else
6633
0
            aRectsString = rectanglesToString(
6634
0
                    convertPixelToLogical(pViewShell->GetViewData(), aRects, aBoundingBox));
6635
6636
0
        if (it == pViewShell)
6637
0
        {
6638
0
            OString sBoundingBoxString = "EMPTY"_ostr;
6639
0
            if (!aBoundingBox.IsEmpty())
6640
0
                sBoundingBoxString = aBoundingBox.toString();
6641
6642
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
6643
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
6644
0
        }
6645
0
        else
6646
0
            SfxLokHelper::notifyOtherView(*it, pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
6647
0
                                          "selection", aRectsString);
6648
0
    }
6649
0
}
6650
6651
namespace
6652
{
6653
6654
void updateLibreOfficeKitAutoFill(const ScViewData& rViewData, tools::Rectangle const & rRectangle, bool bIsTableArea)
6655
0
{
6656
0
    if (!comphelper::LibreOfficeKit::isActive())
6657
0
        return;
6658
6659
0
    double nPPTX = rViewData.GetPPTX();
6660
0
    double nPPTY = rViewData.GetPPTY();
6661
6662
0
    OString sRectangleString = "EMPTY"_ostr;
6663
0
    if (!rRectangle.IsEmpty())
6664
0
    {
6665
        // selection start handle
6666
0
        tools::Rectangle aLogicRectangle(
6667
0
                rRectangle.Left()  / nPPTX, rRectangle.Top() / nPPTY,
6668
0
                rRectangle.Right() / nPPTX, rRectangle.Bottom() / nPPTY);
6669
0
        sRectangleString = aLogicRectangle.toString();
6670
0
    }
6671
6672
0
    ScTabViewShell* pViewShell = rViewData.GetViewShell();
6673
0
    if (bIsTableArea)
6674
0
    {
6675
0
        tools::JsonWriter writer;
6676
0
        writer.put("commandName", "TableAutoFillInfo");
6677
0
        {
6678
0
            const auto aState = writer.startNode("state");
6679
0
            writer.put("rectangle", sRectangleString);
6680
0
        }
6681
0
        OString info = writer.finishAndGetAsOString();
6682
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, info);
6683
0
    }
6684
0
    else
6685
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_AUTO_FILL_AREA, sRectangleString);
6686
0
}
6687
6688
} //end anonymous namespace
6689
6690
void ScGridWindow::UpdateCursorOverlay()
6691
0
{
6692
0
    ScDocument& rDoc = mrViewData.GetDocument();
6693
6694
0
    const MapMode aDrawMode = GetDrawMapMode();
6695
0
    const MapMode aOldMode = GetMapMode();
6696
0
    comphelper::ScopeGuard aMapModeGuard(
6697
0
        [&aOldMode, &aDrawMode, this] {
6698
0
            if (aOldMode != aDrawMode)
6699
0
                SetMapMode(aOldMode);
6700
0
        }
6701
0
    );
6702
0
    if (aOldMode != aDrawMode)
6703
0
        SetMapMode(aDrawMode);
6704
6705
    // Existing OverlayObjects may be transformed in later versions.
6706
    // For now, just re-create them.
6707
6708
0
    DeleteCursorOverlay();
6709
6710
0
    std::vector<tools::Rectangle> aPixelRects;
6711
6712
    //  determine the cursor rectangles in pixels (moved from ScGridWindow::DrawCursor)
6713
6714
0
    SCTAB nTab = mrViewData.CurrentTabForData();
6715
0
    SCCOL nX = mrViewData.GetCurX();
6716
0
    SCROW nY = mrViewData.GetCurY();
6717
6718
0
    const ScPatternAttr* pPattern = rDoc.GetPattern(nX,nY,nTab);
6719
6720
0
    if (!comphelper::LibreOfficeKit::isActive() && !maVisibleRange.isInside(nX, nY))
6721
0
    {
6722
0
        if (maVisibleRange.mnCol2 < nX || maVisibleRange.mnRow2 < nY)
6723
0
            return;     // no further check needed, nothing visible
6724
6725
0
        if (pPattern)
6726
0
        {
6727
            // fdo#87382 Also display the cell cursor for the visible part of
6728
            // merged cells if the view position is part of merged cells.
6729
0
            const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
6730
0
            if (rMerge.GetColMerge() <= 1 && rMerge.GetRowMerge() <= 1)
6731
0
                return;     // not merged and invisible
6732
6733
0
            SCCOL nX2 = nX + rMerge.GetColMerge() - 1;
6734
0
            SCROW nY2 = nY + rMerge.GetRowMerge() - 1;
6735
            // Check if the middle or tail of the merged range is visible.
6736
0
            if (maVisibleRange.mnCol1 > nX2 || maVisibleRange.mnRow1 > nY2)
6737
0
                return;     // no visible part
6738
0
        }
6739
0
    }
6740
6741
    //  don't show the cursor in overlapped cells
6742
0
    const bool bOverlapped = pPattern && pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped();
6743
6744
    //  left or above of the screen?
6745
0
    bool bVis = comphelper::LibreOfficeKit::isActive() || ( nX>=mrViewData.GetPosX(eHWhich) && nY>=mrViewData.GetPosY(eVWhich) );
6746
0
    if (!bVis)
6747
0
    {
6748
0
        SCCOL nEndX = nX;
6749
0
        SCROW nEndY = nY;
6750
0
        if (pPattern)
6751
0
        {
6752
0
            const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
6753
0
            if (rMerge.GetColMerge() > 1)
6754
0
                nEndX += rMerge.GetColMerge()-1;
6755
0
            if (rMerge.GetRowMerge() > 1)
6756
0
                nEndY += rMerge.GetRowMerge()-1;
6757
0
        }
6758
0
        bVis = ( nEndX>=mrViewData.GetPosX(eHWhich) && nEndY>=mrViewData.GetPosY(eVWhich) );
6759
0
    }
6760
6761
0
    if ( bVis && !bOverlapped && !mrViewData.HasEditView(eWhich) && mrViewData.IsActive() )
6762
0
    {
6763
0
        Point aScrPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
6764
0
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6765
6766
        //  completely right of/below the screen?
6767
        //  (test with logical start position in aScrPos)
6768
0
        bool bMaybeVisible;
6769
0
        if ( bLayoutRTL )
6770
0
            bMaybeVisible = ( aScrPos.X() >= -2 && aScrPos.Y() >= -2 );
6771
0
        else
6772
0
        {
6773
0
            Size aOutSize = GetOutputSizePixel();
6774
0
            bMaybeVisible = ( aScrPos.X() <= aOutSize.Width() + 2 && aScrPos.Y() <= aOutSize.Height() + 2 );
6775
0
        }
6776
6777
        // in the tiled rendering case, don't limit to the screen size
6778
0
        if (bMaybeVisible || comphelper::LibreOfficeKit::isActive())
6779
0
        {
6780
0
            tools::Long nSizeXPix;
6781
0
            tools::Long nSizeYPix;
6782
0
            mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
6783
6784
0
            if (bLayoutRTL)
6785
0
                aScrPos.AdjustX( -(nSizeXPix - 2) );       // move instead of mirroring
6786
6787
            // show the cursor as 4 (thin) rectangles
6788
0
            tools::Rectangle aRect(aScrPos, Size(nSizeXPix, nSizeYPix));
6789
6790
0
            float fScaleFactor = GetDPIScaleFactor();
6791
6792
0
            tools::Long aCursorWidth = 1 * fScaleFactor;
6793
6794
0
            tools::Rectangle aLeft = aRect;
6795
0
            aLeft.AdjustTop( -aCursorWidth );
6796
0
            aLeft.AdjustBottom(aCursorWidth );
6797
0
            aLeft.SetRight( aLeft.Left() );
6798
0
            aLeft.AdjustLeft( -aCursorWidth );
6799
6800
0
            tools::Rectangle aRight = aRect;
6801
0
            aRight.AdjustTop( -aCursorWidth );
6802
0
            aRight.AdjustBottom(aCursorWidth );
6803
0
            aRight.SetLeft( aRight.Right() );
6804
0
            aRight.AdjustRight(aCursorWidth );
6805
6806
0
            tools::Rectangle aTop = aRect;
6807
0
            aTop.SetBottom( aTop.Top() );
6808
0
            aTop.AdjustTop( -aCursorWidth );
6809
6810
0
            tools::Rectangle aBottom = aRect;
6811
0
            aBottom.SetTop( aBottom.Bottom() );
6812
0
            aBottom.AdjustBottom(aCursorWidth );
6813
6814
0
            aPixelRects.push_back(aLeft);
6815
0
            aPixelRects.push_back(aRight);
6816
0
            aPixelRects.push_back(aTop);
6817
0
            aPixelRects.push_back(aBottom);
6818
0
        }
6819
0
    }
6820
6821
0
    if ( !aPixelRects.empty() )
6822
0
    {
6823
0
        if (comphelper::LibreOfficeKit::isActive())
6824
0
        {
6825
0
            mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
6826
0
            updateKitCellCursor(nullptr);
6827
0
        }
6828
0
        else
6829
0
        {
6830
            // #i70788# get the OverlayManager safely
6831
0
            rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6832
6833
0
            if (xOverlayManager.is())
6834
0
            {
6835
0
                ScModule* mod = ScModule::get();
6836
0
                Color aCursorColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
6837
0
                if (mrViewData.GetActivePart() != eWhich)
6838
                    // non-active pane uses a different color.
6839
0
                    aCursorColor = mod->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
6840
0
                std::vector< basegfx::B2DRange > aRanges;
6841
0
                const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
6842
6843
                // tdf#143733, tdf#145080 - improve border visibility
6844
                // constants picked for maximum consistency at 100% and adequate response on zoom
6845
                // line width = 1.5 at 100% (0.75 left +/- 0.75 right), 50% = 1, 200% = 1.25, 400% = 2.25
6846
0
                const double fCurZoom(mrViewData.GetZoomX());
6847
0
                const double fMinSize = 0.25 * GetDPIScaleFactor();
6848
0
                const double fAdjust(fMinSize + mrViewData.GetZoomX() * 0.5);
6849
0
                int nAdjustPixel(o3tl::convert(fAdjust, o3tl::Length::pt, o3tl::Length::px));
6850
                // If zoom level is 50% or greater the rectangles must be at least 1 pixel thick
6851
0
                if (fCurZoom >= 0.5)
6852
0
                    nAdjustPixel = std::max(1, nAdjustPixel);
6853
6854
                // Below each rectangle is adjusted so that they have thickness of nAdjustPixel
6855
                // Left rectangle
6856
0
                basegfx::B2DRange aRBLeft(aPixelRects[0].Left() - nAdjustPixel, aPixelRects[0].Top() - nAdjustPixel,
6857
0
                                          aPixelRects[0].Right(), aPixelRects[0].Bottom() + nAdjustPixel);
6858
0
                aRBLeft.transform(aTransform);
6859
0
                aRanges.push_back(aRBLeft);
6860
                // Right rectangle
6861
0
                basegfx::B2DRange aRBRight(aPixelRects[1].Left(), aPixelRects[1].Top() - nAdjustPixel,
6862
0
                                           aPixelRects[1].Right() + nAdjustPixel, aPixelRects[1].Bottom() + nAdjustPixel);
6863
0
                aRBRight.transform(aTransform);
6864
0
                aRanges.push_back(aRBRight);
6865
                // Top rectangle
6866
0
                basegfx::B2DRange aRBTop(aPixelRects[2].Left() - nAdjustPixel, aPixelRects[2].Top() - nAdjustPixel,
6867
0
                                         aPixelRects[2].Right() + nAdjustPixel, aPixelRects[2].Bottom());
6868
0
                aRBTop.transform(aTransform);
6869
0
                aRanges.push_back(aRBTop);
6870
                // Bottom rectangle
6871
0
                basegfx::B2DRange aRBBottom(aPixelRects[3].Left() - nAdjustPixel, aPixelRects[3].Top(),
6872
0
                                            aPixelRects[3].Right() + nAdjustPixel, aPixelRects[3].Bottom() + nAdjustPixel);
6873
0
                aRBBottom.transform(aTransform);
6874
0
                aRanges.push_back(aRBBottom);
6875
6876
0
                std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
6877
0
                    sdr::overlay::OverlayType::Solid,
6878
0
                    aCursorColor,
6879
0
                    std::move(aRanges),
6880
0
                    false, false));
6881
6882
                // Internal white contrast rectangle
6883
0
                std::vector< basegfx::B2DRange > aRangesInternal;
6884
0
                basegfx::B2DRange aRBInternal(aPixelRects[0].Right(), aPixelRects[2].Bottom(),
6885
0
                                              aPixelRects[1].Left() - 1, aPixelRects[3].Top() - 1);
6886
0
                aRBInternal.transform(aTransform);
6887
0
                aRangesInternal.push_back(aRBInternal);
6888
6889
0
                std::unique_ptr<sdr::overlay::OverlayObject> pOverlayInternal(new sdr::overlay::OverlaySelection(
6890
0
                    sdr::overlay::OverlayType::NoFill,
6891
0
                    COL_WHITE,
6892
0
                    std::move(aRangesInternal),
6893
0
                    true, false));
6894
6895
0
                xOverlayManager->add(*pOverlay);
6896
0
                xOverlayManager->add(*pOverlayInternal);
6897
0
                mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
6898
0
                mpOOCursors->append(std::move(pOverlay));
6899
0
                mpOOCursors->append(std::move(pOverlayInternal));
6900
0
            }
6901
0
        }
6902
0
    }
6903
0
}
6904
6905
void ScGridWindow::GetCellSelection(std::vector<tools::Rectangle>& rLogicRects)
6906
0
{
6907
0
    std::vector<tools::Rectangle> aRects;
6908
0
    if (comphelper::LibreOfficeKit::isActive() &&
6909
0
            comphelper::LibreOfficeKit::isCompatFlagSet(
6910
0
                comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6911
0
        GetSelectionRectsPrintTwips(aRects);
6912
0
    else
6913
0
        GetSelectionRects(aRects);
6914
0
    UpdateKitSelection(aRects, &rLogicRects);
6915
0
}
6916
6917
void ScGridWindow::DeleteSelectionOverlay()
6918
0
{
6919
0
    mpOOSelection.reset();
6920
0
}
6921
6922
std::unique_ptr<sdr::overlay::OverlayObject> ScGridWindow::DrawOverlay(const std::vector<tools::Rectangle>& rRects, const Color& rColor, bool bBorder, bool bContrastOutline, sdr::overlay::OverlayType eOverlayType)
6923
0
{
6924
0
    rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6925
0
    if (!xOverlayManager.is())
6926
0
        return std::unique_ptr<sdr::overlay::OverlayObject>();
6927
6928
0
    std::vector< basegfx::B2DRange > aRanges;
6929
0
    const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
6930
0
    ScDocument& rDoc = mrViewData.GetDocument();
6931
0
    SCTAB nTab = mrViewData.CurrentTabForData();
6932
0
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6933
6934
0
    for(const tools::Rectangle & rRA : rRects)
6935
0
    {
6936
0
        if (bLayoutRTL)
6937
0
        {
6938
0
            basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
6939
0
            aRB.transform(aTransform);
6940
0
            aRanges.push_back(aRB);
6941
0
        }
6942
0
        else
6943
0
        {
6944
0
            basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
6945
0
            aRB.transform(aTransform);
6946
0
            aRanges.push_back(aRB);
6947
0
        }
6948
0
    }
6949
6950
0
    std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
6951
0
        eOverlayType,
6952
0
        rColor,
6953
0
        std::move(aRanges),
6954
0
        bBorder, bContrastOutline));
6955
6956
0
    xOverlayManager->add(*pOverlay);
6957
6958
0
    return pOverlay;
6959
0
}
6960
6961
void ScGridWindow::UpdateSelectionOverlay()
6962
0
{
6963
0
    const MapMode aDrawMode = GetDrawMapMode();
6964
0
    const MapMode aOldMode = GetMapMode();
6965
0
    comphelper::ScopeGuard aMapModeGuard(
6966
0
        [&aOldMode, &aDrawMode, this] {
6967
0
            if (aOldMode != aDrawMode)
6968
0
                SetMapMode(aOldMode);
6969
0
        }
6970
0
    );
6971
0
    if (aOldMode != aDrawMode)
6972
0
        SetMapMode(aDrawMode);
6973
6974
0
    DeleteSelectionOverlay();
6975
0
    std::vector<tools::Rectangle> aRects;
6976
0
    if (comphelper::LibreOfficeKit::isActive() &&
6977
0
            comphelper::LibreOfficeKit::isCompatFlagSet(
6978
0
                comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6979
0
        GetSelectionRectsPrintTwips(aRects);
6980
0
    else
6981
0
        GetSelectionRects(aRects);
6982
6983
0
    if (!aRects.empty() && mrViewData.IsActive())
6984
0
    {
6985
        // #i70788# get the OverlayManager safely
6986
0
        rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6987
0
        if (comphelper::LibreOfficeKit::isActive())
6988
0
        {
6989
            // notify the LibreOfficeKit too
6990
0
            UpdateKitSelection(aRects);
6991
0
        }
6992
0
        else
6993
0
        {
6994
0
            const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
6995
0
            std::unique_ptr<sdr::overlay::OverlayObject> pOverlay = DrawOverlay(aRects, aHighlight, true, true, sdr::overlay::OverlayType::Transparent);
6996
0
            if (pOverlay) {
6997
0
                mpOOSelection.reset(new sdr::overlay::OverlayObjectList);
6998
0
                mpOOSelection->append(std::move(pOverlay));
6999
0
            }
7000
0
        }
7001
0
    }
7002
0
    else
7003
0
    {
7004
0
        ScTabViewShell* pViewShell = mrViewData.GetViewShell();
7005
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY"_ostr);
7006
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY"_ostr);
7007
0
        SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
7008
7009
0
        ScInputHandler* pViewHdl = ScModule::get()->GetInputHdl(pViewShell);
7010
0
        if (!pViewHdl || !pViewHdl->IsEditMode())
7011
0
        {
7012
0
            std::vector<ReferenceMark> aReferenceMarks;
7013
0
            ScInputHandler::SendReferenceMarks(pViewShell, aReferenceMarks);
7014
0
        }
7015
0
    }
7016
0
}
7017
7018
void ScGridWindow::DeleteDatabaseOverlay()
7019
0
{
7020
0
    mpDBExpandRect.reset();
7021
0
    mpOODatabase.reset();
7022
0
    if (comphelper::LibreOfficeKit::isActive()) // notify the LibreOfficeKit
7023
0
    {
7024
0
        tools::Rectangle aEmptyRect;
7025
0
        updateLibreOfficeKitAutoFill(mrViewData, aEmptyRect, true);
7026
0
    }
7027
0
}
7028
7029
void ScGridWindow::UpdateDatabaseOverlay()
7030
0
{
7031
0
    const MapMode aDrawMode = GetDrawMapMode();
7032
0
    const MapMode aOldMode = GetMapMode();
7033
0
    comphelper::ScopeGuard aMapModeGuard(
7034
0
        [&aOldMode, &aDrawMode, this] {
7035
0
            if (aOldMode != aDrawMode)
7036
0
                SetMapMode(aOldMode);
7037
0
        }
7038
0
    );
7039
0
    if (aOldMode != aDrawMode)
7040
0
        SetMapMode(aDrawMode);
7041
7042
0
    DeleteDatabaseOverlay();
7043
7044
0
    ScDocument& rDocument = mrViewData.GetDocument();
7045
0
    ScAddress aCurrentAddress = mrViewData.GetCurPos();
7046
0
    std::vector<basegfx::B2DRange> aRanges;
7047
0
    ScRange aCurrRange;
7048
0
    const ScDBData* pDBData = rDocument.GetTableDBAtCursor(aCurrentAddress.Col(), aCurrentAddress.Row(),
7049
0
        aCurrentAddress.Tab(), ScDBDataPortion::AREA);
7050
0
    if (pDBData)
7051
0
    {
7052
0
        pDBData->GetArea(aCurrRange);
7053
0
        Point aStart = mrViewData.GetScrPos(aCurrRange.aStart.Col(),
7054
0
                                            aCurrRange.aStart.Row(), eWhich);
7055
0
        Point aEnd = mrViewData.GetScrPos(aCurrRange.aEnd.Col() + 1,
7056
0
                                          aCurrRange.aEnd.Row() + 1, eWhich);
7057
7058
0
        basegfx::B2DRange aRange(aStart.X(), aStart.Y(), aEnd.X(), aEnd.Y());
7059
0
        const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7060
0
        aRange.transform(aTransform);
7061
0
        aRanges.push_back(aRange);
7062
7063
0
        maDBRange = aCurrRange;
7064
0
    }
7065
7066
0
    if (!aRanges.empty())
7067
0
    {
7068
0
        ScModule* mod = ScModule::get();
7069
0
        Color aDBColor = mod->GetColorConfig().GetColorValue(svtools::CALCDBFOCUS).nColor;
7070
7071
        // #i70788# get the OverlayManager safely
7072
0
        rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7073
0
        if (xOverlayManager.is())
7074
0
        {
7075
0
            std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7076
0
                sdr::overlay::OverlayType::NoFill,
7077
0
                aDBColor,
7078
0
                std::move(aRanges),
7079
0
                true, true));
7080
7081
0
            xOverlayManager->add(*pOverlay);
7082
0
            std::unique_ptr<sdr::overlay::OverlayObjectList> pOverlayList = DrawFillMarker(aCurrRange.aEnd.Col(), aCurrRange.aEnd.Row(), mpDBExpandRect, true);
7083
0
            if (pOverlayList)
7084
0
            {
7085
0
                mpOODatabase.swap(pOverlayList);
7086
0
                mpOODatabase->append(std::move(pOverlay));
7087
0
            }
7088
0
        }
7089
0
    }
7090
0
}
7091
7092
void ScGridWindow::UpdateHighlightOverlay()
7093
0
{
7094
0
    mpOOHighlight.reset();          // DeleteHighlightOverlay
7095
0
    std::vector<tools::Rectangle> aRects;
7096
0
    if (comphelper::LibreOfficeKit::isActive() &&
7097
0
            comphelper::LibreOfficeKit::isCompatFlagSet(
7098
0
                comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
7099
0
        GetRectsAnyFor(mrViewData.GetHighlightData(), aRects, true);
7100
0
    else
7101
0
        GetPixelRectsFor(mrViewData.GetHighlightData(), aRects);
7102
7103
0
    if (!aRects.empty() && mrViewData.IsActive())
7104
0
    {
7105
0
        ScModule* mod = ScModule::get();
7106
0
        const Color aBackgroundColor = mod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
7107
0
        Color aHighlightColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
7108
0
        aHighlightColor.Merge(aBackgroundColor, 100);
7109
7110
        // #i70788# get the OverlayManager safely
7111
0
        std::unique_ptr<sdr::overlay::OverlayObject> pOverlay = DrawOverlay(aRects, aHighlightColor, false, false, sdr::overlay::OverlayType::Transparent);
7112
0
        if (pOverlay)
7113
0
        {
7114
0
            mpOOHighlight.reset(new sdr::overlay::OverlayObjectList);
7115
0
            mpOOHighlight->append(std::move(pOverlay));
7116
0
        }
7117
0
    }
7118
0
}
7119
7120
void ScGridWindow::DeleteAutoFillOverlay()
7121
0
{
7122
0
    mpOOAutoFill.reset();
7123
0
    mpAutoFillRect.reset();
7124
0
}
7125
7126
std::unique_ptr<sdr::overlay::OverlayObjectList> ScGridWindow::DrawFillMarker(SCCOL nX, SCROW nY, std::optional<tools::Rectangle>& rRect, bool bIsTableArea)
7127
0
{
7128
0
    SCTAB nTab = mrViewData.CurrentTabForData();
7129
0
    ScDocument& rDoc = mrViewData.GetDocument();
7130
0
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
7131
7132
    // tdf#162006 Ensures the AutoFill handle is visible at any zoom level
7133
    // At 100% = Total Size 8 (2px for the external white line; 6px for the handle itself)
7134
0
    const double fScaleFactor(2 + 2 * GetDPIScaleFactor());
7135
0
    const double fZoom(2 + 2 * mrViewData.GetZoomX());
7136
0
    Size aFillHandleSize(fScaleFactor + fZoom, fScaleFactor + fZoom);
7137
7138
0
    Point aFillPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
7139
0
    tools::Long nSizeXPix;
7140
0
    tools::Long nSizeYPix;
7141
0
    mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
7142
7143
    // Consider the case of merged cells to determine where to place the AutoFill handle
7144
0
    SCCOL nX2 = mrViewData.GetCurX();
7145
0
    SCCOL nY2 = mrViewData.GetCurY();
7146
0
    const ScMergeAttr& rMerge = rDoc.GetAttr(nX2, nY2, nTab, ATTR_MERGE);
7147
0
    if (rMerge.GetColMerge() >= 1 || rMerge.GetRowMerge() >= 1)
7148
0
    {
7149
0
        nX2 += rMerge.GetColMerge() - 1;
7150
0
        nY2 += rMerge.GetRowMerge() - 1;
7151
0
    }
7152
7153
0
    if (bLayoutRTL && !comphelper::LibreOfficeKit::isActive())
7154
0
        aFillPos.AdjustX( -(nSizeXPix + (aFillHandleSize.Width() / 2)) );
7155
0
    else
7156
0
        aFillPos.AdjustX(nSizeXPix - (aFillHandleSize.Width() / 2) );
7157
7158
0
    aFillPos.AdjustY(nSizeYPix);
7159
0
    aFillPos.AdjustY( -(aFillHandleSize.Height() / 2) );
7160
7161
0
    tools::Rectangle aFillRect(aFillPos, aFillHandleSize);
7162
7163
    // expand rect to increase hit area
7164
0
    rRect = aFillRect;
7165
0
    rRect->expand(fScaleFactor);
7166
7167
    // #i70788# get the OverlayManager safely
7168
0
    rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7169
0
    if (comphelper::LibreOfficeKit::isActive()) // notify the LibreOfficeKit
7170
0
    {
7171
0
        updateLibreOfficeKitAutoFill(mrViewData, aFillRect, bIsTableArea);
7172
0
    }
7173
0
    else if (xOverlayManager.is())
7174
0
    {
7175
0
        const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7176
7177
        // Outer rectangle (always white for contrast)
7178
0
        std::vector< basegfx::B2DRange > aRangesOuter;
7179
0
        basegfx::B2DRange aRBOuter = vcl::unotools::b2DRectangleFromRectangle(aFillRect);
7180
0
        aRBOuter.transform(aTransform);
7181
0
        aRangesOuter.push_back(aRBOuter);
7182
7183
0
        std::unique_ptr<sdr::overlay::OverlayObject> pOverlayOuter(new sdr::overlay::OverlaySelection(
7184
0
            sdr::overlay::OverlayType::Solid,
7185
0
            COL_WHITE,
7186
0
            std::move(aRangesOuter),
7187
0
            false, false));
7188
7189
        // Inner rectangle
7190
0
        std::vector< basegfx::B2DRange > aRangesInner;
7191
0
        tools::Rectangle aRectInner(aFillRect);
7192
0
        aRectInner.AdjustTop(1);
7193
0
        aRectInner.AdjustBottom(-1);
7194
0
        aRectInner.AdjustLeft(1);
7195
0
        aRectInner.AdjustRight(-1);
7196
0
        basegfx::B2DRange aRBInner = vcl::unotools::b2DRectangleFromRectangle(aRectInner);
7197
0
        aRBInner.transform(aTransform);
7198
0
        aRangesInner.push_back(aRBInner);
7199
7200
0
        ScModule* mod = ScModule::get();
7201
0
        Color aHandleColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
7202
0
        if (mrViewData.GetActivePart() != eWhich)
7203
            // non-active pane uses a different color.
7204
0
            aHandleColor = mod->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
7205
7206
0
        std::unique_ptr<sdr::overlay::OverlayObject> pOverlayInner(new sdr::overlay::OverlaySelection(
7207
0
            sdr::overlay::OverlayType::Solid,
7208
0
            aHandleColor,
7209
0
            std::move(aRangesInner),
7210
0
            false, false));
7211
7212
0
        xOverlayManager->add(*pOverlayOuter);
7213
0
        xOverlayManager->add(*pOverlayInner);
7214
0
        std::unique_ptr<sdr::overlay::OverlayObjectList> pOverlayList(new sdr::overlay::OverlayObjectList);
7215
0
        pOverlayList->append(std::move(pOverlayOuter));
7216
0
        pOverlayList->append(std::move(pOverlayInner));
7217
7218
0
        return pOverlayList;
7219
0
    }
7220
0
    return std::unique_ptr<sdr::overlay::OverlayObjectList>();
7221
0
}
7222
7223
void ScGridWindow::UpdateAutoFillOverlay()
7224
0
{
7225
0
    const MapMode aDrawMode = GetDrawMapMode();
7226
0
    const MapMode aOldMode = GetMapMode();
7227
0
    comphelper::ScopeGuard aMapModeGuard(
7228
0
        [&aOldMode, &aDrawMode, this] {
7229
0
            if (aOldMode != aDrawMode)
7230
0
                SetMapMode(aOldMode);
7231
0
        }
7232
0
    );
7233
0
    if (aOldMode != aDrawMode)
7234
0
        SetMapMode(aDrawMode);
7235
7236
0
    DeleteAutoFillOverlay();
7237
7238
    //  get the AutoFill handle rectangle in pixels
7239
7240
0
    if ( !(bAutoMarkVisible && aAutoMarkPos.Tab() == mrViewData.GetTabNumber() &&
7241
0
         !mrViewData.HasEditView(eWhich) && mrViewData.IsActive()) )
7242
0
        return;
7243
7244
0
    SCCOL nX = aAutoMarkPos.Col();
7245
0
    SCROW nY = aAutoMarkPos.Row();
7246
7247
0
    if (!maVisibleRange.isInside(nX, nY) && !comphelper::LibreOfficeKit::isActive())
7248
0
    {
7249
        // Autofill mark is not visible.  Bail out.
7250
0
        return;
7251
0
    }
7252
7253
0
    mpOOAutoFill = DrawFillMarker(nX, nY, mpAutoFillRect, false);
7254
0
}
7255
7256
void ScGridWindow::DeleteDragRectOverlay()
7257
0
{
7258
0
    mpOODragRect.reset();
7259
0
}
7260
7261
void ScGridWindow::UpdateDragRectOverlay()
7262
0
{
7263
0
    bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
7264
0
        comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
7265
7266
0
    const MapMode aDrawMode = GetDrawMapMode();
7267
0
    const MapMode aOldMode = GetMapMode();
7268
0
    comphelper::ScopeGuard aMapModeGuard(
7269
0
        [&aOldMode, &aDrawMode, this] {
7270
0
            if (aOldMode != aDrawMode)
7271
0
                SetMapMode(aOldMode);
7272
0
        }
7273
0
    );
7274
0
    if (aOldMode != aDrawMode)
7275
0
        SetMapMode(aDrawMode);
7276
7277
0
    DeleteDragRectOverlay();
7278
7279
    //  get the rectangles in pixels (moved from DrawDragRect)
7280
7281
0
    if ( bDragRect || bPagebreakDrawn )
7282
0
    {
7283
0
        std::vector<tools::Rectangle> aPixelRects;
7284
7285
0
        SCCOL nX1 = bDragRect ? nDragStartX : aPagebreakDrag.aStart.Col();
7286
0
        SCROW nY1 = bDragRect ? nDragStartY : aPagebreakDrag.aStart.Row();
7287
0
        SCCOL nX2 = bDragRect ? nDragEndX : aPagebreakDrag.aEnd.Col();
7288
0
        SCROW nY2 = bDragRect ? nDragEndY : aPagebreakDrag.aEnd.Row();
7289
7290
0
        SCTAB nTab = mrViewData.CurrentTabForData();
7291
7292
0
        SCCOL nPosX = mrViewData.GetPosX(WhichH(eWhich));
7293
0
        SCROW nPosY = mrViewData.GetPosY(WhichV(eWhich));
7294
0
        if (nX1 < nPosX) nX1 = nPosX;
7295
0
        if (nX2 < nPosX) nX2 = nPosX;
7296
0
        if (nY1 < nPosY) nY1 = nPosY;
7297
0
        if (nY2 < nPosY) nY2 = nPosY;
7298
7299
0
        Point aScrPos(bInPrintTwips ? mrViewData.GetPrintTwipsPos( nX1, nY1 )
7300
0
            : mrViewData.GetScrPos( nX1, nY1, eWhich ) );
7301
7302
0
        tools::Long nSizeXPix=0;
7303
0
        tools::Long nSizeYPix=0;
7304
0
        ScDocument& rDoc = mrViewData.GetDocument();
7305
0
        double nPPTX = mrViewData.GetPPTX();
7306
0
        double nPPTY = mrViewData.GetPPTY();
7307
0
        SCCOLROW i;
7308
7309
0
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
7310
0
        tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
7311
0
        tools::Long nAdjust = comphelper::LibreOfficeKit::isActive() ? 0 : 2;
7312
7313
0
        if (rDoc.ValidCol(nX2) && nX2>=nX1)
7314
0
            for (i=nX1; i<=nX2; i++)
7315
0
            {
7316
0
                tools::Long nWidth = rDoc.GetColWidth( static_cast<SCCOL>(i), nTab );
7317
0
                nSizeXPix += bInPrintTwips ? nWidth : ScViewData::ToPixel( nWidth, nPPTX );
7318
0
            }
7319
0
        else
7320
0
        {
7321
0
            aScrPos.AdjustX( -nLayoutSign );
7322
0
            nSizeXPix   += nAdjust;
7323
0
        }
7324
7325
0
        if (rDoc.ValidRow(nY2) && nY2>=nY1)
7326
0
            for (i=nY1; i<=nY2; i++)
7327
0
            {
7328
0
                tools::Long nHeight = rDoc.GetRowHeight( i, nTab );
7329
0
                nSizeYPix += bInPrintTwips ? nHeight : ScViewData::ToPixel( nHeight, nPPTY );
7330
0
            }
7331
0
        else
7332
0
        {
7333
0
            aScrPos.AdjustY( -1 );
7334
0
            nSizeYPix   += nAdjust;
7335
0
        }
7336
7337
0
        if (comphelper::LibreOfficeKit::isActive())
7338
0
        {
7339
0
            nSizeXPix -= 2;
7340
0
            nSizeYPix -= 2;
7341
0
        }
7342
7343
0
        aScrPos.AdjustX( -(nAdjust * nLayoutSign) );
7344
0
        aScrPos.AdjustY( -1 * nAdjust );
7345
0
        tools::Rectangle aRect( aScrPos.X(), aScrPos.Y(),
7346
0
                         aScrPos.X() + ( nSizeXPix + nAdjust ) * nLayoutSign, aScrPos.Y() + nSizeYPix + nAdjust );
7347
0
        if ( bLayoutRTL )
7348
0
        {
7349
0
            aRect.SetLeft( aRect.Right() );   // end position is left
7350
0
            aRect.SetRight( aScrPos.X() );
7351
0
        }
7352
7353
0
        if ( meDragInsertMode == INS_CELLSDOWN )
7354
0
        {
7355
0
            aPixelRects.emplace_back( aRect.Left()+1, aRect.Top()+3, aRect.Left()+1, aRect.Bottom()-2 );
7356
0
            aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+3, aRect.Right()-1, aRect.Bottom()-2 );
7357
0
            aPixelRects.emplace_back( aRect.Left()+1, aRect.Top(), aRect.Right()-1, aRect.Top()+2 );
7358
0
            aPixelRects.emplace_back( aRect.Left()+1, aRect.Bottom()-1, aRect.Right()-1, aRect.Bottom()-1 );
7359
0
        }
7360
0
        else if ( meDragInsertMode == INS_CELLSRIGHT )
7361
0
        {
7362
0
            aPixelRects.emplace_back( aRect.Left(), aRect.Top()+1, aRect.Left()+2, aRect.Bottom()-1 );
7363
0
            aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+1, aRect.Right()-1, aRect.Bottom()-1 );
7364
0
            aPixelRects.emplace_back( aRect.Left()+3, aRect.Top()+1, aRect.Right()-2, aRect.Top()+1 );
7365
0
            aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-1, aRect.Right()-2, aRect.Bottom()-1 );
7366
0
        }
7367
0
        else
7368
0
        {
7369
0
            aPixelRects.emplace_back( aRect.Left(), aRect.Top(), aRect.Left()+2, aRect.Bottom() );
7370
0
            aPixelRects.emplace_back( aRect.Right()-2, aRect.Top(), aRect.Right(), aRect.Bottom() );
7371
0
            aPixelRects.emplace_back( aRect.Left()+3, aRect.Top(), aRect.Right()-3, aRect.Top()+2 );
7372
0
            aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-2, aRect.Right()-3, aRect.Bottom() );
7373
0
        }
7374
7375
        // #i70788# get the OverlayManager safely
7376
0
        rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7377
7378
0
        if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
7379
0
        {
7380
0
            std::vector< basegfx::B2DRange > aRanges;
7381
0
            const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7382
7383
0
            for(const tools::Rectangle & rRA : aPixelRects)
7384
0
            {
7385
0
                basegfx::B2DRange aRB(rRA.Left(), rRA.Top(), rRA.Right() + 1, rRA.Bottom() + 1);
7386
0
                aRB.transform(aTransform);
7387
0
                aRanges.push_back(aRB);
7388
0
            }
7389
7390
0
            std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7391
0
                sdr::overlay::OverlayType::Invert,
7392
0
                COL_BLACK,
7393
0
                std::move(aRanges),
7394
0
                false, false));
7395
7396
0
            xOverlayManager->add(*pOverlay);
7397
0
            mpOODragRect.reset(new sdr::overlay::OverlayObjectList);
7398
0
            mpOODragRect->append(std::move(pOverlay));
7399
0
        }
7400
7401
0
        ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
7402
0
        if (comphelper::LibreOfficeKit::isActive() && pViewShell)
7403
0
        {
7404
0
            OString aRectsString;
7405
0
            tools::Rectangle aBoundingBox;
7406
7407
0
            std::vector<tools::Rectangle> aRectangles;
7408
0
            aRectangles.push_back(aRect);
7409
7410
0
            if (bInPrintTwips)
7411
0
            {
7412
0
                aBoundingBox = aRect;
7413
0
                aRectsString = rectanglesToString(aRectangles);
7414
0
            }
7415
0
            else
7416
0
            {
7417
0
                aRectsString = rectanglesToString(convertPixelToLogical(pViewShell->GetViewData(), aRectangles, aBoundingBox));
7418
0
            }
7419
7420
0
            OString sBoundingBoxString = "EMPTY"_ostr;
7421
0
            if (!aBoundingBox.IsEmpty())
7422
0
                sBoundingBoxString = aBoundingBox.toString();
7423
7424
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
7425
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
7426
0
        }
7427
0
    }
7428
0
}
7429
7430
void ScGridWindow::DeleteHeaderOverlay()
7431
0
{
7432
0
    mpOOHeader.reset();
7433
0
}
7434
7435
void ScGridWindow::UpdateHeaderOverlay()
7436
0
{
7437
0
    const MapMode aDrawMode = GetDrawMapMode();
7438
0
    const MapMode aOldMode = GetMapMode();
7439
0
    comphelper::ScopeGuard aMapModeGuard(
7440
0
        [&aOldMode, &aDrawMode, this] {
7441
0
            if (aOldMode != aDrawMode)
7442
0
                SetMapMode(aOldMode);
7443
0
        }
7444
0
    );
7445
0
    if (aOldMode != aDrawMode)
7446
0
        SetMapMode(aDrawMode);
7447
7448
0
    DeleteHeaderOverlay();
7449
7450
    //  Pixel rectangle is in aInvertRect
7451
0
    if ( !aInvertRect.IsEmpty() )
7452
0
    {
7453
        // #i70788# get the OverlayManager safely
7454
0
        rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7455
7456
0
        if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
7457
0
        {
7458
            // Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
7459
0
            std::vector< basegfx::B2DRange > aRanges;
7460
0
            const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7461
0
            basegfx::B2DRange aRB(aInvertRect.Left(), aInvertRect.Top(), aInvertRect.Right() + 1, aInvertRect.Bottom() + 1);
7462
7463
0
            aRB.transform(aTransform);
7464
0
            aRanges.push_back(aRB);
7465
7466
0
            std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7467
0
                sdr::overlay::OverlayType::Invert,
7468
0
                COL_BLACK,
7469
0
                std::move(aRanges),
7470
0
                false, false));
7471
7472
0
            xOverlayManager->add(*pOverlay);
7473
0
            mpOOHeader.reset(new sdr::overlay::OverlayObjectList);
7474
0
            mpOOHeader->append(std::move(pOverlay));
7475
0
        }
7476
0
    }
7477
0
}
7478
7479
void ScGridWindow::DeleteShrinkOverlay()
7480
0
{
7481
0
    mpOOShrink.reset();
7482
0
}
7483
7484
void ScGridWindow::UpdateShrinkOverlay()
7485
0
{
7486
0
    const MapMode aDrawMode = GetDrawMapMode();
7487
0
    const MapMode aOldMode = GetMapMode();
7488
0
    comphelper::ScopeGuard aMapModeGuard(
7489
0
        [&aOldMode, &aDrawMode, this] {
7490
0
            if (aOldMode != aDrawMode)
7491
0
                SetMapMode(aOldMode);
7492
0
        }
7493
0
    );
7494
0
    if (aOldMode != aDrawMode)
7495
0
        SetMapMode(aDrawMode);
7496
7497
0
    DeleteShrinkOverlay();
7498
7499
    //  get the rectangle in pixels
7500
7501
0
    tools::Rectangle aPixRect;
7502
0
    ScRange aRange;
7503
0
    SCTAB nTab = mrViewData.CurrentTabForData();
7504
0
    if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() &&
7505
0
         mrViewData.GetDelMark( aRange ) )
7506
0
    {
7507
        //! limit to visible area
7508
0
        if ( aRange.aStart.Col() <= aRange.aEnd.Col() &&
7509
0
             aRange.aStart.Row() <= aRange.aEnd.Row() )
7510
0
        {
7511
0
            Point aStart = mrViewData.GetScrPos( aRange.aStart.Col(),
7512
0
                                                 aRange.aStart.Row(), eWhich );
7513
0
            Point aEnd = mrViewData.GetScrPos( aRange.aEnd.Col()+1,
7514
0
                                               aRange.aEnd.Row()+1, eWhich );
7515
0
            aEnd.AdjustX( -1 );
7516
0
            aEnd.AdjustY( -1 );
7517
7518
0
            aPixRect = tools::Rectangle( aStart,aEnd );
7519
0
        }
7520
0
    }
7521
7522
0
    if ( !aPixRect.IsEmpty() )
7523
0
    {
7524
        // #i70788# get the OverlayManager safely
7525
0
        rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7526
7527
0
        if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
7528
0
        {
7529
0
            std::vector< basegfx::B2DRange > aRanges;
7530
0
            const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7531
0
            basegfx::B2DRange aRB(aPixRect.Left(), aPixRect.Top(), aPixRect.Right() + 1, aPixRect.Bottom() + 1);
7532
7533
0
            aRB.transform(aTransform);
7534
0
            aRanges.push_back(aRB);
7535
7536
0
            std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7537
0
                sdr::overlay::OverlayType::Invert,
7538
0
                COL_BLACK,
7539
0
                std::move(aRanges),
7540
0
                false, false));
7541
7542
0
            xOverlayManager->add(*pOverlay);
7543
0
            mpOOShrink.reset(new sdr::overlay::OverlayObjectList);
7544
0
            mpOOShrink->append(std::move(pOverlay));
7545
0
        }
7546
0
    }
7547
0
}
7548
7549
void ScGridWindow::DeleteSparklineGroupOverlay()
7550
0
{
7551
0
    mpOOSparklineGroup.reset();
7552
0
}
7553
7554
void ScGridWindow::UpdateSparklineGroupOverlay()
7555
0
{
7556
0
    const MapMode aDrawMode = GetDrawMapMode();
7557
0
    const MapMode aOldMode = GetMapMode();
7558
0
    comphelper::ScopeGuard aMapModeGuard(
7559
0
        [&aOldMode, &aDrawMode, this] {
7560
0
            if (aOldMode != aDrawMode)
7561
0
                SetMapMode(aOldMode);
7562
0
        }
7563
0
    );
7564
0
    if (aOldMode != aDrawMode)
7565
0
        SetMapMode(aDrawMode);
7566
7567
0
    DeleteSparklineGroupOverlay();
7568
7569
0
    ScAddress aCurrentAddress = mrViewData.GetCurPos();
7570
7571
0
    ScDocument& rDocument = mrViewData.GetDocument();
7572
0
    if (auto pSparkline = rDocument.GetSparkline(aCurrentAddress))
7573
0
    {
7574
0
        mpOOSparklineGroup.reset(new sdr::overlay::OverlayObjectList);
7575
7576
0
        rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7577
0
        if (xOverlayManager.is())
7578
0
        {
7579
0
            auto* pList = rDocument.GetSparklineList(aCurrentAddress.Tab());
7580
0
            if (pList)
7581
0
            {
7582
0
                auto const aSparklines = pList->getSparklinesFor(pSparkline->getSparklineGroup());
7583
7584
0
                Color aColor = SvtOptionsDrawinglayer::getHilightColor();
7585
7586
0
                std::vector<basegfx::B2DRange> aRanges;
7587
0
                const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7588
7589
0
                for (auto const& pCurrentSparkline : aSparklines)
7590
0
                {
7591
0
                    SCCOL nColumn = pCurrentSparkline->getColumn();
7592
0
                    SCROW nRow = pCurrentSparkline->getRow();
7593
7594
0
                    SCTAB nTab = mrViewData.CurrentTabForData();
7595
0
                    ScRange aCurrRange(nColumn, nRow, nTab);
7596
0
                    bool bMerge = rDocument.IsMerged(aCurrentAddress);
7597
0
                    if (bMerge)
7598
0
                        rDocument.ExtendMerge(aCurrRange);
7599
7600
0
                    Point aStart = mrViewData.GetScrPos(aCurrRange.aStart.Col(),
7601
0
                                                        aCurrRange.aStart.Row(), eWhich);
7602
0
                    Point aEnd = mrViewData.GetScrPos(aCurrRange.aEnd.Col() + 1,
7603
0
                                                      aCurrRange.aEnd.Row() + 1, eWhich);
7604
7605
0
                    basegfx::B2DRange aRange(aStart.X(), aStart.Y(), aEnd.X(), aEnd.Y());
7606
7607
0
                    aRange.transform(aTransform);
7608
0
                    aRanges.push_back(aRange);
7609
0
                }
7610
7611
0
                std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7612
0
                        sdr::overlay::OverlayType::Transparent,
7613
0
                        aColor, std::move(aRanges), true, false));
7614
7615
0
                xOverlayManager->add(*pOverlay);
7616
0
                mpOOSparklineGroup->append(std::move(pOverlay));
7617
0
            }
7618
0
        }
7619
0
    }
7620
0
}
7621
7622
// #i70788# central method to get the OverlayManager safely
7623
rtl::Reference<sdr::overlay::OverlayManager> ScGridWindow::getOverlayManager() const
7624
0
{
7625
0
    SdrPageView* pPV = mrViewData.GetView()->GetScDrawView()->GetSdrPageView();
7626
7627
0
    if(pPV)
7628
0
    {
7629
0
        SdrPageWindow* pPageWin = pPV->FindPageWindow( *GetOutDev() );
7630
7631
0
        if ( pPageWin )
7632
0
        {
7633
0
            return pPageWin->GetOverlayManager();
7634
0
        }
7635
0
    }
7636
7637
0
    return rtl::Reference<sdr::overlay::OverlayManager>();
7638
0
}
7639
7640
ScViewData& ScGridWindow::getViewData()
7641
0
{
7642
0
    return mrViewData;
7643
0
}
7644
7645
FactoryFunction ScGridWindow::GetUITestFactory() const
7646
0
{
7647
0
    return ScGridWinUIObject::create;
7648
0
}
7649
7650
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */