tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / gridwin.cxx
blob2a73bc1857fab9159646bfe0343d6bb350930733
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <scitems.hxx>
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/unit_conversion.hxx>
37 #include <sfx2/dispatch.hxx>
38 #include <sfx2/viewfrm.hxx>
39 #include <sfx2/docfile.hxx>
40 #include <sfx2/ipclient.hxx>
41 #include <svl/stritem.hxx>
42 #include <svl/sharedstringpool.hxx>
43 #include <vcl/canvastools.hxx>
44 #include <vcl/commandevent.hxx>
45 #include <vcl/cursor.hxx>
46 #include <vcl/dialoghelper.hxx>
47 #include <vcl/inputctx.hxx>
48 #include <vcl/settings.hxx>
49 #include <vcl/virdev.hxx>
50 #include <vcl/weldutils.hxx>
51 #include <sot/formats.hxx>
52 #include <comphelper/classids.hxx>
53 #include <comphelper/scopeguard.hxx>
55 #include <svx/svdview.hxx>
56 #include <svx/svdocapt.hxx>
57 #include <svx/svdpagv.hxx>
58 #include <svtools/optionsdrawinglayer.hxx>
60 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
61 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
62 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
63 #include <com/sun/star/sheet/MemberResultFlags.hpp>
64 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
65 #include <com/sun/star/awt/KeyModifier.hpp>
66 #include <com/sun/star/awt/MouseButton.hpp>
67 #include <com/sun/star/script/vba/VBAEventId.hpp>
68 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
69 #include <com/sun/star/text/textfield/Type.hpp>
71 #include <com/sun/star/awt/XVclWindowPeer.hdl>
73 #include <gridwin.hxx>
74 #include <tabvwsh.hxx>
75 #include <docsh.hxx>
76 #include <viewdata.hxx>
77 #include <tabview.hxx>
78 #include <select.hxx>
79 #include <scmod.hxx>
80 #include <document.hxx>
81 #include <attrib.hxx>
82 #include <dbdata.hxx>
83 #include <stlpool.hxx>
84 #include <printfun.hxx>
85 #include <cbutton.hxx>
86 #include <sc.hrc>
87 #include <helpids.h>
88 #include <globstr.hrc>
89 #include <strings.hrc>
90 #include <editutil.hxx>
91 #include <scresid.hxx>
92 #include <inputhdl.hxx>
93 #include <uiitems.hxx>
94 #include <formulacell.hxx>
95 #include <patattr.hxx>
96 #include <notemark.hxx>
97 #include <rfindlst.hxx>
98 #include <output.hxx>
99 #include <docfunc.hxx>
100 #include <dbdocfun.hxx>
101 #include <dpobject.hxx>
102 #include <transobj.hxx>
103 #include <drwtrans.hxx>
104 #include <seltrans.hxx>
105 #include <sizedev.hxx>
106 #include <AccessibilityHints.hxx>
107 #include <dpsave.hxx>
108 #include <viewuno.hxx>
109 #include <compiler.hxx>
110 #include <editable.hxx>
111 #include <fillinfo.hxx>
112 #include <filterentries.hxx>
113 #include <drwlayer.hxx>
114 #include <validat.hxx>
115 #include <tabprotection.hxx>
116 #include <postit.hxx>
117 #include <dpcontrol.hxx>
118 #include <checklistmenu.hxx>
119 #include <clipparam.hxx>
120 #include <overlayobject.hxx>
121 #include <cellsuno.hxx>
122 #include <drawview.hxx>
123 #include <dragdata.hxx>
124 #include <cliputil.hxx>
125 #include <queryentry.hxx>
126 #include <markdata.hxx>
127 #include <externalrefmgr.hxx>
128 #include <spellcheckcontext.hxx>
129 #include <uiobject.hxx>
130 #include <undoblk.hxx>
131 #include <datamapper.hxx>
132 #include <inputopt.hxx>
133 #include <queryparam.hxx>
134 #include <SparklineList.hxx>
136 #include <officecfg/Office/Common.hxx>
138 #include <svx/PaletteManager.hxx>
139 #include <svx/sdrpagewindow.hxx>
140 #include <svx/sdr/overlay/overlaymanager.hxx>
141 #include <vcl/svapp.hxx>
142 #include <vcl/uitest/logger.hxx>
143 #include <vcl/uitest/eventdescription.hxx>
144 #include <svx/sdr/overlay/overlayselection.hxx>
145 #include <comphelper/lok.hxx>
146 #include <sfx2/lokhelper.hxx>
148 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
150 #include <vector>
151 #include <boost/property_tree/json_parser.hpp>
153 #include <FilterListBox.hxx>
155 using namespace css;
156 using namespace css::uno;
158 struct ScGridWindow::MouseEventState
160 bool mbActivatePart;
162 MouseEventState() :
163 mbActivatePart(false)
167 #define SC_FILTERLISTBOX_LINES 12
169 ScGridWindow::VisibleRange::VisibleRange(const ScDocument& rDoc)
170 : mnCol1(0)
171 , mnCol2(rDoc.MaxCol())
172 , mnRow1(0)
173 , mnRow2(rDoc.MaxRow())
177 bool ScGridWindow::VisibleRange::isInside(SCCOL nCol, SCROW nRow) const
179 return mnCol1 <= nCol && nCol <= mnCol2 && mnRow1 <= nRow && nRow <= mnRow2;
182 bool ScGridWindow::VisibleRange::set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
184 bool bChanged = mnCol1 != nCol1 || mnRow1 != nRow1 || mnCol2 != nCol2 || mnRow2 != nRow2;
186 mnCol1 = nCol1;
187 mnRow1 = nRow1;
188 mnCol2 = nCol2;
189 mnRow2 = nRow2;
191 return bChanged;
194 // ListBox in a FloatingWindow (pParent)
195 ScFilterListBox::ScFilterListBox(weld::Window* pParent, ScGridWindow* pGrid,
196 SCCOL nNewCol, SCROW nNewRow, ScFilterBoxMode eNewMode)
197 : xBuilder(Application::CreateBuilder(pParent, u"modules/scalc/ui/filterlist.ui"_ustr))
198 , xPopover(xBuilder->weld_popover(u"FilterList"_ustr))
199 , xTreeView(xBuilder->weld_tree_view(u"list"_ustr))
200 , pGridWin(pGrid)
201 , nCol(nNewCol)
202 , nRow(nNewRow)
203 , bInit(true)
204 , bCancelled(false)
205 , bGridHadMouseCaptured(pGrid->IsMouseCaptured())
206 , nSel(0)
207 , eMode(eNewMode)
208 , nAsyncSelectHdl(nullptr)
210 xTreeView->connect_row_activated(LINK(this, ScFilterListBox, SelectHdl));
211 xTreeView->connect_key_press(LINK(this, ScFilterListBox, KeyInputHdl));
214 ScFilterListBox::~ScFilterListBox()
216 if (nAsyncSelectHdl)
218 Application::RemoveUserEvent(nAsyncSelectHdl);
219 nAsyncSelectHdl = nullptr;
223 void ScFilterListBox::EndInit()
225 sal_Int32 nPos = xTreeView->get_selected_index();
226 if (nPos == -1)
227 nSel = 0;
228 else
229 nSel = nPos;
231 bInit = false;
234 IMPL_LINK(ScFilterListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
236 bool bDone = false;
238 vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
239 // esc with no modifiers
240 if (!aCode.GetModifier() && aCode.GetCode() == KEY_ESCAPE)
242 pGridWin->ClickExtern(); // clears the listbox
243 bDone = true;
246 // nowhere to tab to
247 if (aCode.GetCode() == KEY_TAB)
248 bDone = true;
250 return bDone;
253 IMPL_LINK_NOARG(ScFilterListBox, SelectHdl, weld::TreeView&, bool)
255 if (!bInit && !bCancelled && !nAsyncSelectHdl)
257 int nPos = xTreeView->get_selected_index();
258 if (nPos != -1)
260 nSel = nPos;
261 // #i81298# launch async so the box isn't deleted from modifications within FilterSelect
262 nAsyncSelectHdl = Application::PostUserEvent(LINK(this, ScFilterListBox, AsyncSelectHdl));
265 return true;
268 IMPL_LINK_NOARG(ScFilterListBox, AsyncSelectHdl, void*, void)
270 nAsyncSelectHdl = nullptr;
272 //tdf#133971 hold self-ref until we return
273 auto xThis(shared_from_this());
274 pGridWin->FilterSelect(nSel);
275 if (xThis.use_count() == 1)
277 // tdf#133855 we got disposed by FilterSelect
278 return;
280 pGridWin->ClickExtern();
283 static bool lcl_IsEditableMatrix( ScDocument& rDoc, const ScRange& rRange )
285 // If it is an editable range and if there is a Matrix cell at the bottom right with an
286 // origin top left then the range will be set to contain the exact matrix.
287 //! Extract the MatrixEdges functions directly from the column ???
288 if ( !rDoc.IsBlockEditable( rRange.aStart.Tab(), rRange.aStart.Col(),rRange.aStart.Row(),
289 rRange.aEnd.Col(),rRange.aEnd.Row() ) )
290 return false;
292 ScRefCellValue aCell(rDoc, rRange.aEnd);
293 ScAddress aPos;
294 return (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetMatrixOrigin(rDoc, aPos) && aPos == rRange.aStart);
297 static void lcl_UnLockComment( ScDrawView* pView, const Point& rPos, const ScViewData& rViewData )
299 if (!pView)
300 return;
302 ScDocument& rDoc = rViewData.GetDocument();
303 ScAddress aCellPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
304 ScPostIt* pNote = rDoc.GetNote( aCellPos );
305 SdrObject* pObj = pNote ? pNote->GetCaption() : nullptr;
306 if( pObj && pObj->GetLogicRect().Contains( rPos ) && ScDrawLayer::IsNoteCaption( pObj ) )
308 const ScProtectionAttr* pProtAttr = rDoc.GetAttr( aCellPos, ATTR_PROTECTION );
309 bool bProtectAttr = pProtAttr->GetProtection() || pProtAttr->GetHideCell() ;
310 bool bProtectDoc = rDoc.IsTabProtected( aCellPos.Tab() ) || rViewData.GetSfxDocShell()->IsReadOnly() ;
311 // unlock internal layer (if not protected), will be relocked in ScDrawView::MarkListHasChanged()
312 pView->LockInternalLayer( bProtectDoc && bProtectAttr );
316 static bool lcl_GetHyperlinkCell(
317 ScDocument& rDoc, SCCOL& rPosX, SCROW nPosY, SCTAB nTab, ScRefCellValue& rCell, OUString& rURL )
319 bool bFound = false;
322 ScAddress aPos(rPosX, nPosY, nTab);
323 rCell.assign(rDoc, aPos);
324 if (rCell.isEmpty())
326 if ( rPosX <= 0 )
327 return false; // everything empty to the links
328 else
329 --rPosX; // continue search
331 else
333 const ScPatternAttr* pPattern = rDoc.GetPattern(aPos);
334 if ( !pPattern->GetItem(ATTR_HYPERLINK).GetValue().isEmpty() )
336 rURL = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
337 bFound = true;
339 else if (rCell.getType() == CELLTYPE_EDIT)
340 bFound = true;
341 else if (rCell.getType() == CELLTYPE_FORMULA && rCell.getFormula()->IsHyperLinkCell())
342 bFound = true;
343 else
344 return false; // other cell
347 while ( !bFound );
349 return bFound;
352 static void lcl_GetMirror(Point& rPoint, tools::Rectangle& rRect, const tools::Long nWidth)
354 tools::Long nLeft = rRect.Left();
355 tools::Long nRight = rRect.Right();
356 tools::Long nMirrorPX = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px);
357 tools::Long nMirrorMM = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);
359 rPoint.setX(nMirrorPX - rPoint.X());
360 rRect.SetLeft(nMirrorMM - nRight);
361 rRect.SetRight(nMirrorMM - nLeft);
364 // WB_DIALOGCONTROL needed for UNO-Controls
365 ScGridWindow::ScGridWindow( vcl::Window* pParent, ScViewData& rData, ScSplitPos eWhichPos )
366 : DocWindow( pParent, WB_CLIPCHILDREN | WB_DIALOGCONTROL ),
367 DropTargetHelper( this ),
368 DragSourceHelper( this ),
369 maVisibleRange(rData.GetDocument()),
370 mrViewData( rData ),
371 eWhich( eWhichPos ),
372 nCursorHideCount( 0 ),
373 nButtonDown( 0 ),
374 nMouseStatus( SC_GM_NONE ),
375 nNestedButtonState( ScNestedButtonState::NONE ),
376 nDPField( 0 ),
377 pDragDPObj( nullptr ),
378 nRFIndex( 0 ),
379 nRFAddX( 0 ),
380 nRFAddY( 0 ),
381 nPagebreakMouse( SC_PD_NONE ),
382 nPagebreakBreak( 0 ),
383 nPagebreakPrev( 0 ),
384 nPageScript( SvtScriptType::NONE ),
385 nDragStartX( -1 ),
386 nDragStartY( -1 ),
387 nDragEndX( -1 ),
388 nDragEndY( -1 ),
389 meDragInsertMode( INS_NONE ),
390 aComboButton( GetOutDev() ),
391 aCurMousePos( 0,0 ),
392 nPaintCount( 0 ),
393 aRFSelectedCorned( NONE ),
394 maShowPageBreaksTimer("ScGridWindow maShowPageBreaksTimer"),
395 bEEMouse( false ),
396 bDPMouse( false ),
397 bRFMouse( false ),
398 bRFSize( false ),
399 bPagebreakDrawn( false ),
400 bDragRect( false ),
401 bIsInPaint( false ),
402 bNeedsRepaint( false ),
403 bAutoMarkVisible( false ),
404 bListValButton( false ),
405 m_nDownPosX( -1 ),
406 m_nDownPosY( -1 )
408 set_id(u"grid_window"_ustr);
409 switch(eWhich)
411 case SC_SPLIT_TOPLEFT:
412 eHWhich = SC_SPLIT_LEFT;
413 eVWhich = SC_SPLIT_TOP;
414 break;
415 case SC_SPLIT_TOPRIGHT:
416 eHWhich = SC_SPLIT_RIGHT;
417 eVWhich = SC_SPLIT_TOP;
418 break;
419 case SC_SPLIT_BOTTOMLEFT:
420 eHWhich = SC_SPLIT_LEFT;
421 eVWhich = SC_SPLIT_BOTTOM;
422 break;
423 case SC_SPLIT_BOTTOMRIGHT:
424 eHWhich = SC_SPLIT_RIGHT;
425 eVWhich = SC_SPLIT_BOTTOM;
426 break;
427 default:
428 OSL_FAIL("GridWindow: wrong position");
431 SetUseFrameData(comphelper::LibreOfficeKit::isActive());
432 SetBackground();
434 SetMapMode(mrViewData.GetLogicMode(eWhich));
435 EnableChildTransparentMode();
436 SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );
438 SetHelpId( HID_SC_WIN_GRIDWIN );
440 GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
441 EnableRTL( false );
443 bInitialPageBreaks = true;
444 maShowPageBreaksTimer.SetInvokeHandler(LINK(this, ScGridWindow, InitiatePageBreaksTimer));
445 maShowPageBreaksTimer.SetTimeout(1);
448 ScGridWindow::~ScGridWindow()
450 disposeOnce();
453 void ScGridWindow::dispose()
455 maShowPageBreaksTimer.Stop();
457 ImpDestroyOverlayObjects();
459 mpFilterBox.reset();
460 mpNoteMarker.reset();
461 mpAutoFilterPopup.reset();
462 mpDPFieldPopup.reset();
463 aComboButton.SetOutputDevice(nullptr);
465 if (mpSpellCheckCxt)
466 mpSpellCheckCxt->reset();
467 mpSpellCheckCxt.reset();
469 vcl::Window::dispose();
472 void ScGridWindow::ClickExtern()
476 // #i84277# when initializing the filter box, a Basic error can deactivate the view
477 if (mpFilterBox && mpFilterBox->IsInInit())
478 break;
479 mpFilterBox.reset();
481 while (false);
483 if (mpDPFieldPopup)
485 mpDPFieldPopup->close(false);
486 mpDPFieldPopup.reset();
490 IMPL_LINK_NOARG(ScGridWindow, PopupModeEndHdl, weld::Popover&, void)
492 if (mpFilterBox)
494 bool bMouseWasCaptured = mpFilterBox->MouseWasCaptured();
495 mpFilterBox->SetCancelled(); // cancel select
496 // restore the mouse capture state of the GridWindow to
497 // what it was at initial popup time
498 SAL_WARN_IF(bMouseWasCaptured, "sc.ui", "Is there a scenario where the mouse was captured before mouse down?");
499 if (bMouseWasCaptured)
500 CaptureMouse();
502 GrabFocus();
505 IMPL_LINK( ScGridWindow, PopupSpellingHdl, SpellCallbackInfo&, rInfo, void )
507 if( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
508 mrViewData.GetDispatcher().Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON );
509 else if (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS)
510 mrViewData.GetDispatcher().Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON );
511 else //IGNOREWORD, ADDTODICTIONARY, WORDLANGUAGE, PARALANGUAGE
513 // The spelling status of the word has changed. Close the cell to reset the caches
514 ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
515 if (pHdl)
516 pHdl->EnterHandler();
520 namespace {
522 struct AutoFilterData : public ScCheckListMenuControl::ExtendedData
524 ScAddress maPos;
525 ScDBData* mpData;
528 class AutoFilterAction : public ScCheckListMenuControl::Action
530 protected:
531 VclPtr<ScGridWindow> mpWindow;
532 ScGridWindow::AutoFilterMode meMode;
533 public:
534 AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
535 mpWindow(p), meMode(eMode) {}
536 virtual bool execute() override
538 mpWindow->UpdateAutoFilterFromMenu(meMode);
539 // UpdateAutoFilterFromMenu manually closes the popup so return
540 // false to not attempt a second close
541 return false;
545 class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
547 VclPtr<ScGridWindow> mpWindow;
548 ScAddress maPos;
549 public:
550 AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
551 mpWindow(p), maPos(rPos) {}
552 virtual bool execute() override
554 mpWindow->RefreshAutoFilterButton(maPos);
555 mpWindow->GrabFocus();
556 return false; // this is called after the popup has been closed
560 class AutoFilterSubMenuAction : public AutoFilterAction
562 protected:
563 ScListSubMenuControl* m_pSubMenu;
565 public:
566 AutoFilterSubMenuAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode)
567 : AutoFilterAction(p, eMode)
568 , m_pSubMenu(pSubMenu)
573 class AutoFilterColorAction : public AutoFilterSubMenuAction
575 private:
576 Color m_aColor;
578 public:
579 AutoFilterColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor)
580 : AutoFilterSubMenuAction(p, pSubMenu, eMode)
581 , m_aColor(rColor)
585 virtual bool execute() override
587 const AutoFilterData* pData =
588 static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
590 if (!pData)
591 return false;
593 ScDBData* pDBData = pData->mpData;
594 if (!pDBData)
595 return false;
597 const ScAddress& rPos = pData->maPos;
599 ScViewData& rViewData = m_pSubMenu->GetViewData();
600 ScDocument& rDoc = rViewData.GetDocument();
602 ScQueryParam aParam;
603 pDBData->GetQueryParam(aParam);
605 // Try to use the existing entry for the column (if one exists).
606 ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
608 if (!pEntry)
610 // Something went terribly wrong!
611 return false;
614 if (ScTabViewShell::isAnyEditViewInRange(rViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
615 return false;
617 pEntry->bDoQuery = true;
618 pEntry->nField = rPos.Col();
619 pEntry->eConnect = SC_AND;
621 ScFilterEntries aFilterEntries;
622 rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
624 bool bActive = false;
625 ScQueryEntry::Item& rItem = pEntry->GetQueryItem();
626 if (rItem.maColor == m_aColor
627 && ((meMode == ScGridWindow::AutoFilterMode::TextColor
628 && rItem.meType == ScQueryEntry::ByTextColor)
629 || (meMode == ScGridWindow::AutoFilterMode::BackgroundColor
630 && rItem.meType == ScQueryEntry::ByBackgroundColor)))
632 bActive = true;
635 // Disable color filter when active color was selected
636 if (bActive)
638 aParam.RemoveAllEntriesByField(rPos.Col());
639 pEntry = nullptr; // invalidated by RemoveAllEntriesByField call
641 // tdf#46184 reset filter options to default values
642 aParam.eSearchType = utl::SearchParam::SearchType::Normal;
643 aParam.bCaseSens = false;
644 aParam.bDuplicate = true;
645 aParam.bInplace = true;
647 else
649 if (meMode == ScGridWindow::AutoFilterMode::TextColor)
650 pEntry->SetQueryByTextColor(m_aColor);
651 else
652 pEntry->SetQueryByBackgroundColor(m_aColor);
655 rViewData.GetView()->Query(aParam, nullptr, true);
656 pDBData->SetQueryParam(aParam);
658 return true;
662 class AutoFilterSortColorAction : public AutoFilterSubMenuAction
664 private:
665 Color m_aColor;
666 ScViewData& m_rViewData;
668 public:
669 AutoFilterSortColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor, ScViewData& rViewData)
670 : AutoFilterSubMenuAction(p, pSubMenu, eMode)
671 , m_aColor(rColor)
672 , m_rViewData(rViewData)
676 virtual bool execute() override
678 const AutoFilterData* pData =
679 static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
681 if (!pData)
682 return false;
684 ScDBData* pDBData = pData->mpData;
685 if (!pDBData)
686 return false;
688 const ScAddress& rPos = pData->maPos;
689 SCCOL nCol = rPos.Col();
690 ScSortParam aSortParam;
691 pDBData->GetSortParam(aSortParam);
692 if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
693 // out of bound
694 return false;
696 bool bHasHeader = pDBData->HasHeader();
698 aSortParam.bHasHeader = bHasHeader;
699 aSortParam.bByRow = true;
700 aSortParam.bCaseSens = false;
701 aSortParam.bNaturalSort = false;
702 aSortParam.aDataAreaExtras.mbCellNotes = false;
703 aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
704 aSortParam.aDataAreaExtras.mbCellFormats = true;
705 aSortParam.bInplace = true;
706 aSortParam.maKeyState[0].bDoSort = true;
707 aSortParam.maKeyState[0].nField = nCol;
708 aSortParam.maKeyState[0].bAscending = true;
709 aSortParam.maKeyState[0].aColorSortMode = meMode == ScGridWindow::AutoFilterMode::TextColor
710 ? ScColorSortMode::TextColor
711 : ScColorSortMode::BackgroundColor;
712 aSortParam.maKeyState[0].aColorSortColor = m_aColor;
714 for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
715 aSortParam.maKeyState[i].bDoSort = false;
717 m_rViewData.GetViewShell()->UISort(aSortParam);
719 return true;
723 class AutoFilterColorPopupStartAction : public AutoFilterSubMenuAction
725 private:
726 bool mbIsFilter;
727 public:
728 AutoFilterColorPopupStartAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, bool bIsFilter)
729 : AutoFilterSubMenuAction(p, pSubMenu, ScGridWindow::AutoFilterMode::Normal),
730 mbIsFilter(bIsFilter)
734 virtual bool execute() override
736 const AutoFilterData* pData =
737 static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
739 if (!pData)
740 return false;
742 ScDBData* pDBData = pData->mpData;
743 if (!pDBData)
744 return false;
746 ScViewData& rViewData = m_pSubMenu->GetViewData();
747 ScDocument& rDoc = rViewData.GetDocument();
748 const ScAddress& rPos = pData->maPos;
750 ScFilterEntries aFilterEntries;
751 rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
753 m_pSubMenu->clearMenuItems();
755 XColorListRef xUserColorList;
757 OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
758 PaletteManager aPaletteManager;
759 std::vector<OUString> aPaletteNames = aPaletteManager.GetPaletteList();
760 for (size_t i = 0, nLen = aPaletteNames.size(); i < nLen; ++i)
762 if (aPaletteName == aPaletteNames[i])
764 aPaletteManager.SetPalette(i);
765 xUserColorList = XPropertyList::AsColorList(
766 XPropertyList::CreatePropertyListFromURL(
767 XPropertyListType::Color, aPaletteManager.GetSelectedPalettePath()));
768 if (!xUserColorList->Load())
769 xUserColorList = nullptr;
770 break;
774 ScQueryParam aParam;
775 pDBData->GetQueryParam(aParam);
776 ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
778 int nMenu = 0;
779 for (auto eMode : {ScGridWindow::AutoFilterMode::BackgroundColor, ScGridWindow::AutoFilterMode::TextColor})
781 std::set<Color> aColors = eMode == ScGridWindow::AutoFilterMode::TextColor
782 ? aFilterEntries.getTextColors()
783 : aFilterEntries.getBackgroundColors();
785 for (auto& rColor : aColors)
787 bool bActive = false;
789 if (pEntry)
791 ScQueryEntry::Item& rItem = pEntry->GetQueryItem();
792 if (rItem.maColor == rColor
793 && ((eMode == ScGridWindow::AutoFilterMode::TextColor
794 && rItem.meType == ScQueryEntry::ByTextColor)
795 || (eMode == ScGridWindow::AutoFilterMode::BackgroundColor
796 && rItem.meType == ScQueryEntry::ByBackgroundColor)))
798 bActive = true;
802 const bool bAutoColor = rColor == COL_AUTO;
804 // ColorListBox::ShowPreview is similar
805 ScopedVclPtr<VirtualDevice> xDev(m_pSubMenu->create_virtual_device());
806 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
807 Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
808 xDev->SetOutputSize(aImageSize);
809 const tools::Rectangle aRect(Point(0, 0), aImageSize);
811 if (bAutoColor)
813 const Color aW(COL_WHITE);
814 const Color aG(0xef, 0xef, 0xef);
815 int nMinDim = std::min(aImageSize.Width(), aImageSize.Height()) + 1;
816 int nCheckSize = nMinDim / 3;
817 xDev->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), std::min(nCheckSize, 8), aW, aG);
818 xDev->SetFillColor();
820 else
821 xDev->SetFillColor(rColor);
823 xDev->SetLineColor(rStyleSettings.GetDisableColor());
824 xDev->DrawRect(aRect);
826 if (bAutoColor)
828 OUString sText = eMode == ScGridWindow::AutoFilterMode::TextColor
829 ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
830 : ScResId(SCSTR_FILTER_NO_FILL);
831 if (mbIsFilter)
833 m_pSubMenu->addMenuColorItem(
834 sText, bActive, *xDev, nMenu,
835 new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
837 else
839 m_pSubMenu->addMenuColorItem(
840 sText, bActive, *xDev, nMenu,
841 new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor, rViewData));
844 else
846 OUString sName;
848 bool bFoundColorName = false;
849 if (xUserColorList)
851 sal_Int32 nPos = xUserColorList->GetIndexOfColor(rColor);
852 if (nPos != -1)
854 XColorEntry* pColorEntry = xUserColorList->GetColor(nPos);
855 sName = pColorEntry->GetName();
856 bFoundColorName = true;
859 if (!bFoundColorName)
860 sName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
862 if (mbIsFilter)
864 m_pSubMenu->addMenuColorItem(
865 sName, bActive, *xDev, nMenu,
866 new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
868 else
870 m_pSubMenu->addMenuColorItem(
871 sName, bActive, *xDev, nMenu,
872 new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor,
873 rViewData));
878 ++nMenu;
881 m_pSubMenu->resizeToFitMenuItems();
883 return false;
887 class AddItemToEntry
889 ScQueryEntry::QueryItemsType& mrItems;
890 svl::SharedStringPool& mrPool;
891 public:
892 AddItemToEntry(ScQueryEntry::QueryItemsType& rItems, svl::SharedStringPool& rPool) :
893 mrItems(rItems), mrPool(rPool) {}
894 void operator() (const ScCheckListMenuControl::ResultEntry& rEntry)
896 if (rEntry.bValid)
898 ScQueryEntry::Item aNew;
899 aNew.maString = mrPool.intern(rEntry.aName);
900 // set the filter type to ByValue, if the filter condition is value
901 aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : rEntry.bValue ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
902 aNew.mfVal = rEntry.nValue;
903 mrItems.push_back(aNew);
908 class AddSelectedItemString
910 std::unordered_set<OUString>& mrSetString;
911 std::unordered_set<double>& mrSetValue;
912 public:
913 explicit AddSelectedItemString(std::unordered_set<OUString>& rString, std::unordered_set<double>& rValue) :
914 mrSetString(rString), mrSetValue(rValue) {}
916 void operator() (const ScQueryEntry::Item& rItem)
918 if( rItem.meType == ScQueryEntry::QueryType::ByValue )
919 mrSetValue.insert(rItem.mfVal);
920 else
921 mrSetString.insert(rItem.maString.getString());
925 void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUString& aevent)
927 EventDescription aDescription;
928 aDescription.aAction = "LAUNCH";
929 aDescription.aID = "grid_window";
930 aDescription.aParameters = {{aevent, ""},
931 {"ROW", aRow}, {"COL", aCol}};
932 aDescription.aParent = "MainWindow";
933 aDescription.aKeyWord = "ScGridWinUIObject";
935 UITestLogger::getInstance().logEvent(aDescription);
940 void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow)
942 SCTAB nTab = mrViewData.GetTabNo();
943 ScDocument& rDoc = mrViewData.GetDocument();
944 bool bLOKActive = comphelper::LibreOfficeKit::isActive();
946 mpAutoFilterPopup.reset();
948 // Estimate the width (in pixels) of the longest text in the list
949 ScFilterEntries aFilterEntries;
950 rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);
952 weld::Window* pPopupParent = GetFrameWeld();
953 int nColWidth = ScViewData::ToPixel(rDoc.GetColWidth(nCol, nTab), mrViewData.GetPPTX());
954 mpAutoFilterPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
955 aFilterEntries.mbHasDates, nColWidth));
957 int nMaxTextWidth = 0;
958 if (aFilterEntries.size() <= 10)
960 // do pixel calculation for all elements of short lists
961 for (const auto& rEntry : aFilterEntries)
963 const OUString& aText = rEntry.GetString();
964 nMaxTextWidth = std::max<int>(nMaxTextWidth, mpAutoFilterPopup->GetTextWidth(aText) + aText.getLength() * 2);
967 else
969 // find the longest string, probably it will be the longest rendered text, too
970 // (performance optimization for long lists)
971 auto itMax = aFilterEntries.begin();
972 for (auto it = itMax; it != aFilterEntries.end(); ++it)
974 int nTextWidth = it->GetString().getLength();
975 if (nMaxTextWidth < nTextWidth)
977 nMaxTextWidth = nTextWidth;
978 itMax = it;
981 nMaxTextWidth = mpAutoFilterPopup->GetTextWidth(itMax->GetString()) + nMaxTextWidth * 2;
984 // window should be at least as wide as the column, or the longest text + checkbox, scrollbar ... (it is estimated with 70 pixel now)
985 // window should be maximum 1024 pixel wide.
986 int nWindowWidth = std::min<int>(1024, nMaxTextWidth + 70);
987 nWindowWidth = mpAutoFilterPopup->IncreaseWindowWidthToFitText(nWindowWidth);
988 nMaxTextWidth = std::max<int>(nMaxTextWidth, nWindowWidth - 70);
990 mpAutoFilterPopup->setOKAction(new AutoFilterAction(this, AutoFilterMode::Normal));
991 mpAutoFilterPopup->setPopupEndAction(
992 new AutoFilterPopupEndAction(this, ScAddress(nCol, nRow, nTab)));
993 std::unique_ptr<AutoFilterData> pData(new AutoFilterData);
994 pData->maPos = ScAddress(nCol, nRow, nTab);
996 Point aPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
997 tools::Long nSizeX = 0;
998 tools::Long nSizeY = 0;
999 mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
1000 if (bLOKActive)
1002 // Reverse the zoom factor from aPos and nSize[X|Y]
1003 // before letting the autofilter window convert the to twips
1004 // with no zoom information.
1005 double fZoomX(mrViewData.GetZoomX());
1006 double fZoomY(mrViewData.GetZoomY());
1007 aPos.setX(aPos.getX() / fZoomX);
1008 aPos.setY(aPos.getY() / fZoomY);
1009 nSizeX = nSizeX / fZoomX;
1010 nSizeY = nSizeY / fZoomY;
1012 tools::Rectangle aCellRect(bLOKActive ? aPos : OutputToScreenPixel(aPos), Size(nSizeX, nSizeY));
1014 ScDBData* pDBData = rDoc.GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA);
1015 if (!pDBData)
1016 return;
1018 pData->mpData = pDBData;
1019 mpAutoFilterPopup->setExtendedData(std::move(pData));
1021 ScQueryParam aParam;
1022 pDBData->GetQueryParam(aParam);
1023 std::vector<ScQueryEntry*> aEntries = aParam.FindAllEntriesByField(nCol);
1024 std::unordered_set<OUString> aSelectedString;
1025 std::unordered_set<double> aSelectedValue;
1026 bool bQueryByNonEmpty = aEntries.size() == 1 && aEntries[0]->IsQueryByNonEmpty();
1028 if (!bQueryByNonEmpty)
1030 for (ScQueryEntry* pEntry : aEntries)
1032 if (pEntry && pEntry->eOp == SC_EQUAL)
1034 ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
1035 std::for_each(rItems.begin(), rItems.end(), AddSelectedItemString(aSelectedString, aSelectedValue));
1040 // Populate the check box list.
1041 mpAutoFilterPopup->setMemberSize(aFilterEntries.size());
1042 for (auto it = aFilterEntries.begin(); it != aFilterEntries.end(); ++it)
1044 // tdf#140745 show (empty) entry on top of the checkbox list if not hidden by filter
1045 if (it->GetString().isEmpty() && !it->IsHiddenByFilter())
1047 const OUString& aStringVal = it->GetString();
1048 const double aDoubleVal = it->GetValue();
1049 bool bSelected = true;
1050 if (!aSelectedValue.empty() || !aSelectedString.empty())
1051 bSelected = aSelectedString.count(aStringVal) > 0;
1052 else if (bQueryByNonEmpty)
1053 bSelected = false;
1054 // it->IsHiddenByFilter() is always false here so no need to evaluate it
1055 mpAutoFilterPopup->addMember(aStringVal, aDoubleVal, bSelected, false);
1056 aFilterEntries.maStrData.erase(it);
1057 break;
1060 for (const auto& rEntry : aFilterEntries)
1062 const OUString& aStringVal = rEntry.GetString();
1063 const double aDoubleVal = rEntry.GetValue();
1064 const double aRDoubleVal = rEntry.GetRoundedValue();
1065 bool bSelected = !rEntry.IsHiddenByFilter();
1067 if (!aSelectedValue.empty() || !aSelectedString.empty())
1069 if (rEntry.GetStringType() == ScTypedStrData::Value)
1071 if (aDoubleVal == aRDoubleVal)
1072 bSelected = aSelectedValue.count(aDoubleVal) > 0
1073 || aSelectedString.count(aStringVal) > 0;
1074 else
1075 bSelected = aSelectedValue.count(aDoubleVal) > 0
1076 || aSelectedValue.count(aRDoubleVal) > 0
1077 || aSelectedString.count(aStringVal) > 0;
1079 else
1080 bSelected = aSelectedString.count(aStringVal) > 0;
1083 if ( rEntry.IsDate() )
1084 mpAutoFilterPopup->addDateMember( aStringVal, rEntry.GetValue(), bSelected, rEntry.IsHiddenByFilter());
1085 else
1086 mpAutoFilterPopup->addMember( aStringVal, aRDoubleVal, bSelected, rEntry.IsHiddenByFilter(),
1087 rEntry.GetStringType() == ScTypedStrData::Value );
1090 // Populate the menu.
1091 mpAutoFilterPopup->addMenuItem(
1092 ScResId(STR_MENU_SORT_ASC),
1093 new AutoFilterAction(this, AutoFilterMode::SortAscending));
1094 mpAutoFilterPopup->addMenuItem(
1095 ScResId(STR_MENU_SORT_DESC),
1096 new AutoFilterAction(this, AutoFilterMode::SortDescending));
1097 if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_SORT_COLOR), true, true))
1098 pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, false));
1099 mpAutoFilterPopup->addSeparator();
1100 if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_COLOR), true, true))
1101 pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, true));
1102 if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_CONDITION), true, false))
1104 pSubMenu->addMenuItem(
1105 ScResId(SCSTR_FILTER_EMPTY), new AutoFilterAction(this, AutoFilterMode::Empty));
1106 pSubMenu->addMenuItem(
1107 ScResId(SCSTR_FILTER_NOTEMPTY), new AutoFilterAction(this, AutoFilterMode::NonEmpty));
1108 pSubMenu->addMenuItem(
1109 ScResId(SCSTR_TOP10FILTER), new AutoFilterAction(this, AutoFilterMode::Top10));
1110 pSubMenu->addMenuItem(
1111 ScResId(SCSTR_BOTTOM10FILTER), new AutoFilterAction(this, AutoFilterMode::Bottom10));
1112 pSubMenu->addSeparator();
1113 pSubMenu->addMenuItem(
1114 ScResId(SCSTR_STDFILTER), new AutoFilterAction(this, AutoFilterMode::Custom));
1115 pSubMenu->resizeToFitMenuItems();
1117 if (aEntries.size())
1118 mpAutoFilterPopup->addMenuItem(
1119 ScResId(SCSTR_CLEAR_FILTER), new AutoFilterAction(this, AutoFilterMode::Clear));
1121 mpAutoFilterPopup->initMembers(nMaxTextWidth + 20); // 20 pixel estimated for the checkbox
1123 ScCheckListMenuControl::Config aConfig;
1124 aConfig.mbAllowEmptySet = false;
1125 aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
1126 mpAutoFilterPopup->setConfig(aConfig);
1127 if (IsMouseCaptured())
1128 ReleaseMouse();
1129 mpAutoFilterPopup->launch(pPopupParent, aCellRect);
1131 // remember filter rules before modification
1132 mpAutoFilterPopup->getResult(aSaveAutoFilterResult);
1134 collectUIInformation(OUString::number(nRow), OUString::number(nCol),u"AUTOFILTER"_ustr);
1137 void ScGridWindow::RefreshAutoFilterButton(const ScAddress& rPos)
1139 if (mpFilterButton)
1141 bool bFilterActive = IsAutoFilterActive(rPos.Col(), rPos.Row(), rPos.Tab());
1142 mpFilterButton->setHasHiddenMember(bFilterActive);
1143 mpFilterButton->setPopupPressed(false);
1144 mpFilterButton->draw();
1148 void ScGridWindow::UpdateAutoFilterFromMenu(AutoFilterMode eMode)
1150 // Terminate autofilter popup now when there is no further user input needed
1151 bool bColorMode = eMode == AutoFilterMode::TextColor || eMode == AutoFilterMode::BackgroundColor;
1152 if (!bColorMode)
1153 mpAutoFilterPopup->terminateAllPopupMenus();
1155 const AutoFilterData* pData =
1156 static_cast<const AutoFilterData*>(mpAutoFilterPopup->getExtendedData());
1158 if (!pData)
1159 return;
1161 const ScAddress& rPos = pData->maPos;
1162 ScDBData* pDBData = pData->mpData;
1163 if (!pDBData)
1164 return;
1166 ScDocument& rDoc = mrViewData.GetDocument();
1167 svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
1168 switch (eMode)
1170 case AutoFilterMode::SortAscending:
1171 case AutoFilterMode::SortDescending:
1173 SCCOL nCol = rPos.Col();
1174 ScSortParam aSortParam;
1175 pDBData->GetSortParam(aSortParam);
1176 if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
1177 // out of bound
1178 return;
1180 bool bHasHeader = pDBData->HasHeader();
1182 aSortParam.bHasHeader = bHasHeader;
1183 aSortParam.bByRow = true;
1184 aSortParam.bCaseSens = false;
1185 aSortParam.bNaturalSort = false;
1186 aSortParam.aDataAreaExtras.mbCellNotes = false;
1187 aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
1188 aSortParam.aDataAreaExtras.mbCellFormats = true;
1189 aSortParam.bInplace = true;
1190 aSortParam.maKeyState[0].bDoSort = true;
1191 aSortParam.maKeyState[0].nField = nCol;
1192 aSortParam.maKeyState[0].bAscending = (eMode == AutoFilterMode::SortAscending);
1193 aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1195 for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
1196 aSortParam.maKeyState[i].bDoSort = false;
1198 mrViewData.GetViewShell()->UISort(aSortParam);
1199 return;
1201 case AutoFilterMode::Custom:
1203 ScRange aRange;
1204 pDBData->GetArea(aRange);
1205 mrViewData.GetView()->MarkRange(aRange);
1206 mrViewData.GetView()->SetCursor(rPos.Col(), rPos.Row());
1207 mrViewData.GetDispatcher().Execute(SID_FILTER, SfxCallMode::SLOT | SfxCallMode::RECORD);
1208 return;
1210 default:
1214 ScQueryParam aParam;
1215 pDBData->GetQueryParam(aParam);
1217 if (eMode == AutoFilterMode::Normal)
1219 // Do not recreate autofilter rules if there are no changes from the user
1220 ScCheckListMenuControl::ResultType aResult;
1221 mpAutoFilterPopup->getResult(aResult);
1223 if (aResult == aSaveAutoFilterResult)
1225 SAL_INFO("sc.ui", "Apply autofilter to data when entries are the same");
1227 if (!mpAutoFilterPopup->isAllSelected())
1229 // Apply autofilter to data
1230 ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
1231 pEntry->bDoQuery = true;
1232 pEntry->nField = rPos.Col();
1233 pEntry->eConnect = SC_AND;
1234 pEntry->eOp = SC_EQUAL;
1235 mrViewData.GetView()->Query(aParam, nullptr, true);
1238 return;
1242 // Remove old entries in auto-filter rules
1243 if (!bColorMode)
1245 aParam.RemoveAllEntriesByField(rPos.Col());
1247 // tdf#46184 reset filter options to default values
1248 aParam.eSearchType = utl::SearchParam::SearchType::Normal;
1249 aParam.bCaseSens = false;
1250 aParam.bDuplicate = true;
1251 aParam.bInplace = true;
1254 if (eMode != AutoFilterMode::Clear
1255 && !(eMode == AutoFilterMode::Normal && mpAutoFilterPopup->isAllSelected()))
1257 // Try to use the existing entry for the column (if one exists).
1258 ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
1260 if (!pEntry)
1261 // Something went terribly wrong!
1262 return;
1264 if (ScTabViewShell::isAnyEditViewInRange(mrViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
1265 return;
1267 pEntry->bDoQuery = true;
1268 pEntry->nField = rPos.Col();
1269 pEntry->eConnect = SC_AND;
1271 switch (eMode)
1273 case AutoFilterMode::Normal:
1275 pEntry->eOp = SC_EQUAL;
1277 ScCheckListMenuControl::ResultType aResult;
1278 mpAutoFilterPopup->getResult(aResult);
1280 ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
1281 rItems.clear();
1282 std::for_each(aResult.begin(), aResult.end(), AddItemToEntry(rItems, rPool));
1284 break;
1285 case AutoFilterMode::Top10:
1286 pEntry->eOp = SC_TOPVAL;
1287 pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
1288 pEntry->GetQueryItem().maString = rPool.intern(u"10"_ustr);
1289 break;
1290 case AutoFilterMode::Bottom10:
1291 pEntry->eOp = SC_BOTVAL;
1292 pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
1293 pEntry->GetQueryItem().maString = rPool.intern(u"10"_ustr);
1294 break;
1295 case AutoFilterMode::Empty:
1296 pEntry->SetQueryByEmpty();
1297 break;
1298 case AutoFilterMode::NonEmpty:
1299 pEntry->SetQueryByNonEmpty();
1300 break;
1301 case AutoFilterMode::TextColor:
1302 case AutoFilterMode::BackgroundColor:
1303 assert(false && "should be handled by AutoFilterColorAction::execute");
1304 break;
1305 break;
1306 default:
1307 // We don't know how to handle this!
1308 return;
1312 mrViewData.GetView()->Query(aParam, nullptr, true);
1313 pDBData->SetQueryParam(aParam);
1316 namespace {
1318 void getCellGeometry(Point& rScrPos, Size& rScrSize, const ScViewData& rViewData, SCCOL nCol, SCROW nRow, ScSplitPos eWhich)
1320 // Get the screen position of the cell.
1321 rScrPos = rViewData.GetScrPos(nCol, nRow, eWhich);
1323 // Get the screen size of the cell.
1324 tools::Long nSizeX, nSizeY;
1325 rViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
1326 rScrSize = Size(nSizeX-1, nSizeY-1);
1331 void ScGridWindow::LaunchPageFieldMenu( SCCOL nCol, SCROW nRow )
1333 if (nCol == 0)
1334 // We assume that the page field button is located in cell to the immediate left.
1335 return;
1337 SCTAB nTab = mrViewData.GetTabNo();
1338 ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
1339 if (!pDPObj)
1340 return;
1342 Point aScrPos;
1343 Size aScrSize;
1344 getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
1345 bool bLOK = comphelper::LibreOfficeKit::isActive();
1346 DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol-1, nRow, nTab), pDPObj);
1349 void ScGridWindow::LaunchDPFieldMenu( SCCOL nCol, SCROW nRow )
1351 SCTAB nTab = mrViewData.GetTabNo();
1352 ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
1353 if (!pDPObj)
1354 return;
1356 Point aScrPos;
1357 Size aScrSize;
1358 getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
1359 bool bLOK = comphelper::LibreOfficeKit::isActive();
1360 DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol, nRow, nTab), pDPObj);
1363 void ScGridWindow::ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL)
1365 auto nSizeX = rCellRect.GetWidth();
1367 // minimum width in pixel
1368 if (comphelper::LibreOfficeKit::isActive())
1370 const tools::Long nMinLOKWinWidth = o3tl::convert(STD_COL_WIDTH * 13 / 10, o3tl::Length::twip, o3tl::Length::px);
1371 if (nSizeX < nMinLOKWinWidth)
1372 nSizeX = nMinLOKWinWidth;
1375 weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1376 int nEntryCount = rFilterBox.n_children();
1377 if (nEntryCount > SC_FILTERLISTBOX_LINES)
1378 nEntryCount = SC_FILTERLISTBOX_LINES;
1379 auto nHeight = rFilterBox.get_height_rows(nEntryCount);
1380 rFilterBox.set_size_request(-1, nHeight);
1381 Size aSize(rFilterBox.get_preferred_size());
1382 auto nMaxToExpandTo = std::min(nSizeX, static_cast<decltype(nSizeX)>(300)); // do not over do it (Pixel)
1383 if (aSize.Width() < nMaxToExpandTo)
1384 aSize.setWidth(nMaxToExpandTo);
1386 aSize.AdjustWidth(4); // add a little margin
1387 nSizeX += 4;
1388 aSize.AdjustHeight(4);
1390 tools::Rectangle aCellRect(rCellRect);
1391 aCellRect.AdjustLeft(-2); // offset the little margin above
1393 if (!bLayoutRTL && aSize.Width() > nSizeX)
1395 // move popup position
1396 tools::Long nDiff = aSize.Width() - nSizeX;
1397 tools::Long nNewX = aCellRect.Left() - nDiff;
1398 if ( nNewX < 0 )
1399 nNewX = 0;
1400 aCellRect.SetLeft( nNewX );
1403 rFilterBox.set_size_request(aSize.Width(), aSize.Height());
1405 if (IsMouseCaptured())
1406 ReleaseMouse();
1407 mpFilterBox->popup_at_rect(pParent, aCellRect);
1410 void ScGridWindow::DoScenarioMenu( const ScRange& rScenRange )
1412 bool bMenuAtTop = true;
1414 ScDocument& rDoc = mrViewData.GetDocument();
1415 mpFilterBox.reset();
1417 SCCOL nCol = rScenRange.aEnd.Col(); // Cell is below the Buttons
1418 SCROW nRow = rScenRange.aStart.Row();
1419 if (nRow == 0)
1421 nRow = rScenRange.aEnd.Row() + 1; // Range at very the top -> Button below
1422 if (nRow>rDoc.MaxRow()) nRow = rDoc.MaxRow();
1423 bMenuAtTop = false;
1426 SCTAB nTab = mrViewData.GetTabNo();
1427 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1429 tools::Long nSizeX = 0;
1430 tools::Long nSizeY = 0;
1431 mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
1432 // The button height should not use the merged cell height, should still use single row height
1433 nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
1434 Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
1435 if ( bLayoutRTL )
1436 aPos.AdjustX( -nSizeX );
1437 tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
1438 aCellRect.AdjustTop( -nSizeY );
1439 aCellRect.AdjustBottom( -(nSizeY - 1) );
1440 if (!bMenuAtTop)
1442 Size aButSize = mrViewData.GetScenButSize();
1443 aCellRect.AdjustBottom(aButSize.Height());
1446 // Place the ListBox directly below the black line of the cell grid
1447 // (It looks odd if the line gets hidden...)
1449 weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
1450 mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::Scenario);
1451 mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
1452 weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1453 rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
1455 // Listbox fill
1456 rFilterBox.freeze();
1457 OUString aCurrent;
1458 OUString aTabName;
1459 SCTAB nTabCount = rDoc.GetTableCount();
1460 for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
1462 if (rDoc.HasScenarioRange( i, rScenRange ))
1463 if (rDoc.GetName( i, aTabName ))
1465 rFilterBox.append_text(aTabName);
1466 if (rDoc.IsActiveScenario(i))
1467 aCurrent = aTabName;
1470 rFilterBox.thaw();
1472 ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
1474 rFilterBox.grab_focus();
1476 sal_Int32 nPos = -1;
1477 if (!aCurrent.isEmpty())
1479 nPos = rFilterBox.find_text(aCurrent);
1481 if (nPos == -1 && rFilterBox.n_children() > 0 )
1483 nPos = 0;
1485 if (nPos != -1)
1487 rFilterBox.set_cursor(nPos);
1488 rFilterBox.select(nPos);
1490 mpFilterBox->EndInit();
1493 void ScGridWindow::LaunchDataSelectMenu(const SCCOL nCol, const SCROW nRow)
1495 mpFilterBox.reset();
1497 ScDocument& rDoc = mrViewData.GetDocument();
1498 const SCTAB nTab = mrViewData.GetTabNo();
1499 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1501 tools::Long nSizeX = 0;
1502 tools::Long nSizeY = 0;
1503 mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
1504 Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
1505 bool bLOKActive = comphelper::LibreOfficeKit::isActive();
1507 if (bLOKActive)
1509 // aPos is now view-zoom adjusted and in pixels an more importantly this is pixel aligned to the view-zoom,
1510 // but once we use this to set the position of the floating window, it has no information of view-zoom level
1511 // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, MapMode(MapUnit::MapTwip)) employed in
1512 // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position which will again get zoom scaled in the
1513 // client (effective double scaling) causing wrong positioning/size.
1514 double fZoomX(mrViewData.GetZoomX());
1515 double fZoomY(mrViewData.GetZoomY());
1516 aPos.setX(aPos.getX() / fZoomX);
1517 aPos.setY(aPos.getY() / fZoomY);
1518 nSizeX = nSizeX / fZoomX;
1519 nSizeY = nSizeY / fZoomY;
1522 if ( bLayoutRTL )
1523 aPos.AdjustX( -nSizeX );
1524 tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
1526 weld::Window* pParent = comphelper::LibreOfficeKit::isActive() ? GetFrameWeld() : weld::GetPopupParent(*this, aCellRect);
1527 mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::DataSelect);
1528 mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
1529 weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1530 rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
1532 // SetSize later
1534 const sal_uInt32 nIndex = rDoc.GetAttr(nCol, nRow, nTab, ATTR_VALIDDATA)->GetValue();
1535 const ScValidationData* pData = nIndex ? rDoc.GetValidationEntry(nIndex) : nullptr;
1537 bool bEmpty = false;
1538 std::vector<ScTypedStrData> aStrings; // case sensitive
1539 // Fill List
1540 rDoc.GetDataEntries(nCol, nRow, nTab, aStrings, true /* bValidation */);
1542 // IsIgnoreBlank allows blank values. Don't add empty string unless "Allow Empty Cells"
1543 if (pData && !pData->IsIgnoreBlank())
1545 auto lambda = [](const ScTypedStrData& rStr) { return rStr.GetString().isEmpty(); };
1546 std::erase_if(aStrings, lambda);
1549 if (aStrings.empty())
1550 bEmpty = true;
1552 if (!bEmpty)
1554 rFilterBox.freeze();
1556 // Fill Listbox
1557 bool bWait = aStrings.size() > 100;
1559 if (bWait)
1560 EnterWait();
1562 for (const auto& rString : aStrings)
1564 const OUString& rFilterString = rString.GetString();
1565 rFilterBox.append_text(rFilterString);
1568 if (bWait)
1569 LeaveWait();
1571 rFilterBox.thaw();
1573 ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
1576 sal_Int32 nSelPos = -1;
1578 if ( nIndex )
1580 if (pData)
1582 std::unique_ptr<ScTypedStrData> pNew;
1583 OUString aDocStr = rDoc.GetString(nCol, nRow, nTab);
1584 if ( rDoc.HasValueData( nCol, nRow, nTab ) )
1586 double fVal = rDoc.GetValue(ScAddress(nCol, nRow, nTab));
1587 pNew.reset(new ScTypedStrData(aDocStr, fVal, fVal, ScTypedStrData::Value));
1589 else
1590 pNew.reset(new ScTypedStrData(aDocStr, 0.0, 0.0, ScTypedStrData::Standard));
1592 if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
1594 auto it = std::lower_bound(aStrings.begin(), aStrings.end(), *pNew, ScTypedStrData::LessCaseSensitive());
1595 if (it != aStrings.end() && ScTypedStrData::EqualCaseSensitive()(*it, *pNew))
1596 nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
1598 else
1600 auto it = std::find_if(aStrings.begin(), aStrings.end(), FindTypedStrData(*pNew, true));
1601 if (it != aStrings.end())
1602 nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
1607 // Do not show an empty selection List:
1609 if ( bEmpty )
1611 mpFilterBox.reset();
1613 else
1615 rFilterBox.grab_focus();
1617 if (rFilterBox.n_children())
1619 if (nSelPos != -1)
1620 rFilterBox.set_cursor(nSelPos);
1621 else
1622 rFilterBox.set_cursor(0);
1624 // Select only after GrabFocus, so that the focus rectangle gets correct
1625 if (nSelPos != -1)
1626 rFilterBox.select(nSelPos);
1627 else
1628 rFilterBox.unselect_all();
1630 mpFilterBox->EndInit();
1632 collectUIInformation(OUString::number(nRow), OUString::number(nCol),u"SELECTMENU"_ustr);
1635 void ScGridWindow::FilterSelect( sal_uLong nSel )
1637 weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1638 OUString aString = rFilterBox.get_text(static_cast<sal_Int32>(nSel));
1640 SCCOL nCol = mpFilterBox->GetCol();
1641 SCROW nRow = mpFilterBox->GetRow();
1642 switch (mpFilterBox->GetMode())
1644 case ScFilterBoxMode::DataSelect:
1645 ExecDataSelect(nCol, nRow, aString);
1646 break;
1647 case ScFilterBoxMode::Scenario:
1648 mrViewData.GetView()->UseScenario(aString);
1649 break;
1652 // coverity[check_after_deref] - could be destroyed by ExecDataSelect
1653 if (mpFilterBox)
1654 mpFilterBox->popdown();
1656 GrabFocus(); // Otherwise the focus would be wrong on OS/2
1659 void ScGridWindow::ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr )
1661 ScInputHandler* pViewHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
1662 if (pViewHdl && mrViewData.HasEditView(mrViewData.GetActivePart()))
1663 pViewHdl->CancelHandler();
1665 SCTAB nTab = mrViewData.GetTabNo();
1666 ScViewFunc* pView = mrViewData.GetView();
1667 pView->EnterData( nCol, nRow, nTab, rStr );
1669 // #i52307# CellContentChanged is not in EnterData so it isn't called twice
1670 // if the cursor is moved afterwards.
1671 pView->CellContentChanged();
1674 void ScGridWindow::MoveMouseStatus( ScGridWindow& rDestWin )
1676 if (nButtonDown)
1678 rDestWin.nButtonDown = nButtonDown;
1679 rDestWin.nMouseStatus = nMouseStatus;
1682 if (bRFMouse)
1684 rDestWin.bRFMouse = bRFMouse;
1685 rDestWin.bRFSize = bRFSize;
1686 rDestWin.nRFIndex = nRFIndex;
1687 rDestWin.nRFAddX = nRFAddX;
1688 rDestWin.nRFAddY = nRFAddY;
1689 bRFMouse = false;
1692 if (nPagebreakMouse)
1694 rDestWin.nPagebreakMouse = nPagebreakMouse;
1695 rDestWin.nPagebreakBreak = nPagebreakBreak;
1696 rDestWin.nPagebreakPrev = nPagebreakPrev;
1697 rDestWin.aPagebreakSource = aPagebreakSource;
1698 rDestWin.aPagebreakDrag = aPagebreakDrag;
1699 nPagebreakMouse = SC_PD_NONE;
1703 bool ScGridWindow::TestMouse( const MouseEvent& rMEvt, bool bAction )
1705 // MouseEvent buttons must only be checked if bAction==TRUE
1706 // to allow changing the mouse pointer in MouseMove,
1707 // but not start AutoFill with right button (#74229#).
1708 // with bAction==sal_True, SetFillMode / SetDragMode is called
1710 if ( bAction && !rMEvt.IsLeft() )
1711 return false;
1713 bool bNewPointer = false;
1715 SfxInPlaceClient* pClient = mrViewData.GetViewShell()->GetIPClient();
1716 bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
1718 if ( mrViewData.IsActive() && !bOleActive && !mrViewData.GetViewShell()->IsLokReadOnlyView())
1720 ScDocument& rDoc = mrViewData.GetDocument();
1721 SCTAB nTab = mrViewData.GetTabNo();
1722 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1724 // Auto-Fill
1726 ScRange aMarkRange;
1727 if (mrViewData.GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE)
1729 if (aMarkRange.aStart.Tab() == mrViewData.GetTabNo() && mpAutoFillRect)
1731 Point aMousePos = rMEvt.GetPosPixel();
1732 if (mpAutoFillRect->Contains(aMousePos))
1734 SetPointer( PointerStyle::Cross ); //! bold cross ?
1735 if (bAction)
1737 SCCOL nX = aMarkRange.aEnd.Col();
1738 SCROW nY = aMarkRange.aEnd.Row();
1740 if ( lcl_IsEditableMatrix( mrViewData.GetDocument(), aMarkRange ) )
1741 mrViewData.SetDragMode(
1742 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY, ScFillMode::MATRIX );
1743 else
1744 mrViewData.SetFillMode(
1745 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY );
1747 // The simple selection must also be recognized when dragging,
1748 // where the Marking flag is set and MarkToSimple won't work anymore.
1749 mrViewData.GetMarkData().MarkToSimple();
1751 bNewPointer = true;
1756 // Embedded rectangle
1758 if (rDoc.IsEmbedded())
1760 ScRange aRange;
1761 rDoc.GetEmbedded( aRange );
1762 if ( mrViewData.GetTabNo() == aRange.aStart.Tab() )
1764 Point aStartPos = mrViewData.GetScrPos( aRange.aStart.Col(), aRange.aStart.Row(), eWhich );
1765 Point aEndPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich );
1766 Point aMousePos = rMEvt.GetPosPixel();
1767 if ( bLayoutRTL )
1769 aStartPos.AdjustX(2 );
1770 aEndPos.AdjustX(2 );
1772 bool bTop = ( aMousePos.X() >= aStartPos.X()-3 && aMousePos.X() <= aStartPos.X()+1 &&
1773 aMousePos.Y() >= aStartPos.Y()-3 && aMousePos.Y() <= aStartPos.Y()+1 );
1774 bool bBottom = ( aMousePos.X() >= aEndPos.X()-3 && aMousePos.X() <= aEndPos.X()+1 &&
1775 aMousePos.Y() >= aEndPos.Y()-3 && aMousePos.Y() <= aEndPos.Y()+1 );
1776 if ( bTop || bBottom )
1778 SetPointer( PointerStyle::Cross );
1779 if (bAction)
1781 ScFillMode nMode = bTop ? ScFillMode::EMBED_LT : ScFillMode::EMBED_RB;
1782 mrViewData.SetDragMode(
1783 aRange.aStart.Col(), aRange.aStart.Row(),
1784 aRange.aEnd.Col(), aRange.aEnd.Row(), nMode );
1786 bNewPointer = true;
1792 if (!bNewPointer && bAction)
1794 mrViewData.ResetFillMode();
1797 return bNewPointer;
1800 void ScGridWindow::MouseButtonDown( const MouseEvent& rMEvt )
1802 if (SfxLokHelper::getDeviceFormFactor() == LOKDeviceFormFactor::MOBILE)
1804 ScViewFunc* pView = mrViewData.GetView();
1805 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
1806 bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
1808 Point aPos(rMEvt.GetPosPixel());
1809 SCCOL nPosX;
1810 SCROW nPosY;
1811 mrViewData.GetPosFromPixel(aPos.X(), aPos.Y(), eWhich, nPosX, nPosY);
1813 if (bRefMode && pView->GetFunctionSet().CheckRefBounds(nPosX, nPosY))
1814 return;
1817 nNestedButtonState = ScNestedButtonState::Down;
1819 MouseEventState aState;
1820 HandleMouseButtonDown(rMEvt, aState);
1821 if (aState.mbActivatePart)
1822 mrViewData.GetView()->ActivatePart(eWhich);
1824 if ( nNestedButtonState == ScNestedButtonState::Up )
1826 // #i41690# If an object is deactivated from MouseButtonDown, it might reschedule,
1827 // so MouseButtonUp comes before the MouseButtonDown call is finished. In this case,
1828 // simulate another MouseButtonUp call, so the selection state is consistent.
1830 nButtonDown = rMEvt.GetButtons();
1831 FakeButtonUp();
1833 if ( IsTracking() )
1834 EndTracking(); // normally done in VCL as part of MouseButtonUp handling
1836 nNestedButtonState = ScNestedButtonState::NONE;
1839 void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventState& rState )
1841 // We have to check if a context menu is shown and we have an UI
1842 // active inplace client. In that case we have to ignore the event.
1843 // Otherwise we would crash (context menu has been
1844 // opened by inplace client and we would deactivate the inplace client,
1845 // the context menu is closed by VCL asynchronously which in the end
1846 // would work on deleted objects or the context menu has no parent anymore)
1847 SfxViewShell* pViewSh = mrViewData.GetViewShell();
1848 SfxInPlaceClient* pClient = pViewSh->GetIPClient();
1849 if ( pClient &&
1850 pClient->IsObjectInPlaceActive() &&
1851 vcl::IsInPopupMenuExecute() )
1852 return;
1854 aCurMousePos = rMEvt.GetPosPixel();
1856 // Filter popup is ended with its own mouse click, not when clicking into the Grid Window,
1857 // so the following query is no longer necessary:
1858 ClickExtern(); // deletes FilterBox when available
1860 HideNoteMarker();
1862 bEEMouse = false;
1864 ScModule* pScMod = ScModule::get();
1865 if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
1866 return;
1868 const bool bWasMouseCaptured = IsMouseCaptured();
1869 SAL_WARN_IF(bWasMouseCaptured, "sc.ui", "Is there a scenario where the mouse is captured before mouse down?");
1871 pScActiveViewShell = mrViewData.GetViewShell(); // if left is clicked
1872 nScClickMouseModifier = rMEvt.GetModifier(); // to always catch a control click
1874 bool bDetective = mrViewData.GetViewShell()->IsAuditShell();
1875 bool bRefMode = mrViewData.IsRefMode(); // Start reference
1876 bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
1877 bool bEditMode = mrViewData.HasEditView(eWhich); // also in Mode==SC_INPUT_TYPE
1878 bool bDouble = (rMEvt.GetClicks() == 2);
1879 ScDocument& rDoc = mrViewData.GetDocument();
1880 bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
1882 // DeactivateIP does only happen when MarkListHasChanged
1884 // An error message can show up during GrabFocus call
1885 // (for instance when renaming tables per sheet title)
1887 if ( !nButtonDown || !bDouble ) // single (first) click is always valid
1888 nButtonDown = rMEvt.GetButtons(); // set nButtonDown first, so StopMarking works
1890 if ( ( bEditMode && mrViewData.GetActivePart() == eWhich ) || !bFormulaMode )
1891 GrabFocus();
1893 // #i31846# need to cancel a double click if the first click has set the "ignore" state,
1894 // but a single (first) click is always valid
1895 if ( nMouseStatus == SC_GM_IGNORE && bDouble )
1897 nButtonDown = 0;
1898 nMouseStatus = SC_GM_NONE;
1899 return;
1902 if ( bDetective ) // Detectiv fill mode
1904 if ( rMEvt.IsLeft() && !rMEvt.GetModifier() )
1906 Point aPos = rMEvt.GetPosPixel();
1907 SCCOL nPosX;
1908 SCROW nPosY;
1909 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1911 SfxInt16Item aPosXItem( SID_RANGE_COL, nPosX );
1912 SfxInt32Item aPosYItem( SID_RANGE_ROW, nPosY );
1913 mrViewData.GetDispatcher().ExecuteList(SID_FILL_SELECT,
1914 SfxCallMode::SLOT | SfxCallMode::RECORD,
1915 { &aPosXItem, &aPosYItem });
1918 nButtonDown = 0;
1919 nMouseStatus = SC_GM_NONE;
1920 return;
1923 if (!bDouble)
1924 nMouseStatus = SC_GM_NONE;
1926 rState.mbActivatePart = !bFormulaMode; // Don't activate when in formula mode.
1928 if (bFormulaMode)
1930 ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
1931 pSelEng->SetWindow(this);
1932 pSelEng->SetWhich(eWhich);
1933 pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
1936 if (bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()))
1938 Point aPos = rMEvt.GetPosPixel();
1939 SCCOL nPosX;
1940 SCROW nPosY;
1941 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1943 EditView* pEditView;
1944 SCCOL nEditCol;
1945 SCROW nEditRow;
1946 mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
1947 SCCOL nEndCol = mrViewData.GetEditEndCol();
1948 SCROW nEndRow = mrViewData.GetEditEndRow();
1949 SCCOL nStartCol = mrViewData.GetEditStartCol();
1951 if ( nPosX >= nStartCol && nPosX <= nEndCol &&
1952 nPosY >= nEditRow && nPosY <= nEndRow )
1954 // when clicking in the table EditView, always reset the focus
1955 if (bFormulaMode) // otherwise this has already happen above
1956 GrabFocus();
1958 pScMod->SetInputMode( SC_INPUT_TABLE );
1959 bEEMouse = true;
1961 if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.GetTabNo()))
1963 Point aMouse = rMEvt.GetPosPixel();
1964 tools::Rectangle aOutputArea = pEditView->GetOutputArea();
1965 comphelper::ScopeGuard aOutputGuard(
1966 [pEditView, aOutputArea] {
1967 pEditView->SetOutputArea(aOutputArea);
1970 lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
1971 pEditView->SetOutputArea(aOutputArea);
1973 MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
1974 rMEvt.GetButtons(), rMEvt.GetModifier());
1975 pEditView->MouseButtonDown( aEvent );
1977 else
1978 pEditView->MouseButtonDown( rMEvt );
1979 return;
1983 if (pScMod->GetIsWaterCan())
1985 //! what's up with the Mac ???
1986 if ( rMEvt.GetModifier() + rMEvt.GetButtons() == MOUSE_RIGHT )
1988 nMouseStatus = SC_GM_WATERUNDO;
1989 return;
1993 // Order that matches the displayed Cursor:
1994 // RangeFinder, AutoFill, PageBreak, Drawing
1996 RfCorner rCorner = NONE;
1997 bool bFound = HitRangeFinder(rMEvt.GetPosPixel(), rCorner, &nRFIndex, &nRFAddX, &nRFAddY);
1998 bRFSize = (rCorner != NONE);
1999 aRFSelectedCorned = rCorner;
2001 if (bFound)
2003 bRFMouse = true; // the other variables are initialized above
2005 rState.mbActivatePart = true; // always activate ?
2006 StartTracking();
2007 return;
2010 bool bCrossPointer = TestMouse( rMEvt, true );
2011 if ( bCrossPointer )
2013 if ( bDouble )
2014 mrViewData.GetView()->FillCrossDblClick();
2015 else
2016 pScMod->InputEnterHandler(); // Autofill etc.
2019 if ( !bCrossPointer )
2021 nPagebreakMouse = HitPageBreak( rMEvt.GetPosPixel(), &aPagebreakSource,
2022 &nPagebreakBreak, &nPagebreakPrev );
2023 if (nPagebreakMouse)
2025 bPagebreakDrawn = false;
2026 StartTracking();
2027 PagebreakMove( rMEvt, false );
2028 return;
2032 // in the tiled rendering case, single clicks into drawing objects take
2033 // precedence over bEditMode
2034 if (((!bFormulaMode && !bEditMode) || bIsTiledRendering) && rMEvt.IsLeft())
2036 if ( !bCrossPointer && DrawMouseButtonDown(rMEvt) )
2038 return;
2041 mrViewData.GetViewShell()->SetDrawShell( false ); // no Draw-object selected
2043 // TestMouse has already happened above
2046 Point aPos = rMEvt.GetPosPixel();
2047 SCCOL nPosX;
2048 SCROW nPosY;
2049 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2050 SCTAB nTab = mrViewData.GetTabNo();
2051 m_nDownPosX = nPosX;
2052 m_nDownPosY = nPosY;
2054 // FIXME: this is to limit the number of rows handled in the Online
2055 // to 1000; this will be removed again when the performance
2056 // bottlenecks are sorted out
2057 if ( comphelper::LibreOfficeKit::isActive() && nPosY > MAXTILEDROW - 1 )
2059 nButtonDown = 0;
2060 nMouseStatus = SC_GM_NONE;
2061 return;
2064 // Auto filter / pivot table / data select popup. This shouldn't activate the part.
2066 if ( !bDouble && !bFormulaMode && rMEvt.IsLeft() )
2068 SCCOL nRealPosX;
2069 SCROW nRealPosY;
2070 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nRealPosX, nRealPosY, false );//the real row/col
2072 bool bAutoFilterDisable = false;
2073 bool bPivotDisable = false;
2075 if (rDoc.IsTabProtected(nTab))
2077 const ScTableProtection* pTabProtection = rDoc.GetTabProtection(nTab);
2078 bAutoFilterDisable = pTabProtection && !pTabProtection->isOptionEnabled(ScTableProtection::AUTOFILTER);//autofilter
2079 bPivotDisable = pTabProtection && !pTabProtection->isOptionEnabled(ScTableProtection::PIVOT_TABLES);//pivot
2082 // show in the merged cells the filter of the first cell (nPosX instead of nRealPosX)
2083 const ScMergeFlagAttr* pRealPosAttr = rDoc.GetAttr(nPosX, nRealPosY, nTab, ATTR_MERGE_FLAG);
2085 if (!bAutoFilterDisable && pRealPosAttr->HasAutoFilter())
2087 pScMod->InputEnterHandler();
2088 if (DoAutoFilterButton(nPosX, nRealPosY, rMEvt))
2089 return;
2092 const ScMergeFlagAttr* pAttr = rDoc.GetAttr(nPosX, nPosY, nTab, ATTR_MERGE_FLAG);
2093 if (!bAutoFilterDisable && pAttr->HasAutoFilter())
2095 if (DoAutoFilterButton(nPosX, nPosY, rMEvt))
2097 rState.mbActivatePart = false;
2098 return;
2102 if (!bPivotDisable && (pAttr->HasPivotButton() || pAttr->HasPivotPopupButton() ||
2103 pAttr->HasPivotMultiFieldPopupButton()))
2105 DoPushPivotButton(nPosX, nPosY, rMEvt, pAttr->HasPivotButton(),
2106 pAttr->HasPivotPopupButton(), pAttr->HasPivotMultiFieldPopupButton());
2107 rState.mbActivatePart = false;
2108 return;
2111 if (!bPivotDisable && pAttr->HasPivotToggle())
2113 DoPushPivotToggle(nPosX, nPosY, rMEvt);
2114 rState.mbActivatePart = false;
2117 // List Validity drop-down button
2119 if ( bListValButton )
2121 tools::Rectangle aButtonRect = GetListValButtonRect( aListValPos );
2122 if ( aButtonRect.Contains( aPos ) )
2124 // tdf#149609 if we captured the mouse in the course of this function
2125 // release it before showing the data select menu to undo any unhelpful
2126 // seleng capture
2127 if (!bWasMouseCaptured && IsMouseCaptured())
2128 ReleaseMouse();
2130 LaunchDataSelectMenu( aListValPos.Col(), aListValPos.Row() );
2132 nMouseStatus = SC_GM_FILTER; // not set in DoAutoFilterMenue for bDataSelect
2133 rState.mbActivatePart = false;
2134 return;
2139 // scenario selection
2141 ScRange aScenRange;
2142 if ( rMEvt.IsLeft() && HasScenarioButton( aPos, aScenRange ) )
2144 // tdf#149609 if we captured the mouse in the course of this function
2145 // release it before showing the data scenario menu to undo any unhelpful
2146 // seleng capture
2147 if (!bWasMouseCaptured && IsMouseCaptured())
2148 ReleaseMouse();
2150 DoScenarioMenu( aScenRange );
2152 // Scenario selection comes from MouseButtonDown:
2153 // The next MouseMove on the FilterBox is like a ButtonDown
2154 nMouseStatus = SC_GM_FILTER;
2155 return;
2158 // double click started ?
2160 // StopMarking can be called from DrawMouseButtonDown
2162 if ( nMouseStatus != SC_GM_IGNORE && !bRefMode )
2164 if ( bDouble && !bCrossPointer )
2166 if (nMouseStatus == SC_GM_TABDOWN)
2167 nMouseStatus = SC_GM_DBLDOWN;
2169 else
2170 nMouseStatus = SC_GM_TABDOWN;
2173 // links in the edit cell
2175 bool bAlt = rMEvt.IsMod2();
2176 if ( !bAlt && rMEvt.IsLeft() && ScGlobal::ShouldOpenURL() &&
2177 GetEditUrl(rMEvt.GetPosPixel()) ) // click on link: do not move cursor
2179 SetPointer( PointerStyle::RefHand );
2180 nMouseStatus = SC_GM_URLDOWN; // also only execute when ButtonUp
2181 return;
2184 // Gridwin - Selection Engine
2186 if ( !rMEvt.IsLeft() )
2187 return;
2189 ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
2190 pSelEng->SetWindow(this);
2191 pSelEng->SetWhich(eWhich);
2192 pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
2194 // SelMouseButtonDown on the View is still setting the bMoveIsShift flag
2195 if ( mrViewData.GetView()->SelMouseButtonDown( rMEvt ) )
2197 if (IsMouseCaptured())
2199 // Tracking instead of CaptureMouse, so it can be canceled cleanly
2200 //! Someday SelectionEngine should call StartTracking on its own!?!
2201 ReleaseMouse();
2202 StartTracking();
2204 mrViewData.GetMarkData().SetMarking(true);
2205 return;
2209 void ScGridWindow::MouseButtonUp( const MouseEvent& rMEvt )
2211 aCurMousePos = rMEvt.GetPosPixel();
2212 ScDocument& rDoc = mrViewData.GetDocument();
2213 ScMarkData& rMark = mrViewData.GetMarkData();
2214 // #i41690# detect a MouseButtonUp call from within MouseButtonDown
2215 // (possible through Reschedule from storing an OLE object that is deselected)
2217 if ( nNestedButtonState == ScNestedButtonState::Down )
2218 nNestedButtonState = ScNestedButtonState::Up;
2220 if (nButtonDown != rMEvt.GetButtons())
2221 nMouseStatus = SC_GM_IGNORE; // reset and return
2223 nButtonDown = 0;
2225 if (nMouseStatus == SC_GM_IGNORE)
2227 nMouseStatus = SC_GM_NONE;
2228 // Selection engine: cancel selection
2229 mrViewData.GetView()->GetSelEngine()->Reset();
2230 rMark.SetMarking(false);
2231 if (mrViewData.IsAnyFillMode())
2233 mrViewData.GetView()->StopRefMode();
2234 mrViewData.ResetFillMode();
2236 StopMarking();
2237 DrawEndAction(); // cancel selection/moving in drawing layer
2238 ReleaseMouse();
2239 return;
2242 if (nMouseStatus == SC_GM_FILTER)
2244 nMouseStatus = SC_GM_NONE;
2245 ReleaseMouse();
2246 return; // nothing more should happen here
2249 ScModule* pScMod = ScModule::get();
2250 if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
2251 return;
2253 SfxBindings& rBindings = mrViewData.GetBindings();
2254 if (bEEMouse && mrViewData.HasEditView( eWhich ))
2256 EditView* pEditView;
2257 SCCOL nEditCol;
2258 SCROW nEditRow;
2259 mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2261 if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.GetTabNo()))
2263 Point aMouse = rMEvt.GetPosPixel();
2264 tools::Rectangle aOutputArea = pEditView->GetOutputArea();
2265 comphelper::ScopeGuard aOutputGuard(
2266 [pEditView, aOutputArea] {
2267 pEditView->SetOutputArea(aOutputArea);
2270 lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
2271 pEditView->SetOutputArea(aOutputArea);
2273 MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
2274 rMEvt.GetButtons(), rMEvt.GetModifier());
2275 pEditView->MouseButtonUp( aEvent );
2277 else
2278 pEditView->MouseButtonUp( rMEvt );
2280 if ( rMEvt.IsMiddle() &&
2281 GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
2283 // EditView may have pasted from selection
2284 pScMod->InputChanged( pEditView );
2286 else
2287 pScMod->InputSelection( pEditView ); // parentheses etc.
2289 mrViewData.GetView()->InvalidateAttribs();
2290 rBindings.Invalidate( SID_HYPERLINK_GETLINK );
2291 bEEMouse = false;
2292 return;
2295 if (bDPMouse)
2297 DPMouseButtonUp( rMEvt ); // resets bDPMouse
2298 return;
2301 if (bRFMouse)
2303 RFMouseMove( rMEvt, true ); // Again the proper range
2304 bRFMouse = false;
2305 SetPointer( PointerStyle::Arrow );
2306 ReleaseMouse();
2307 return;
2310 if (nPagebreakMouse)
2312 PagebreakMove( rMEvt, true );
2313 nPagebreakMouse = SC_PD_NONE;
2314 SetPointer( PointerStyle::Arrow );
2315 ReleaseMouse();
2316 return;
2319 if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode
2321 SfxUndoManager* pMgr = mrViewData.GetDocShell()->GetUndoManager();
2322 if ( pMgr->GetUndoActionCount() && dynamic_cast<ScUndoSelectionStyle*>(pMgr->GetUndoAction()) )
2323 pMgr->Undo();
2324 return;
2327 if (DrawMouseButtonUp(rMEvt)) // includes format paint brush handling for drawing objects
2329 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2330 SfxBindings& rFrmBindings=pViewShell->GetViewFrame().GetBindings();
2331 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_WIDTH);
2332 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_HEIGHT);
2333 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
2334 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
2335 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ANGLE);
2336 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_X);
2337 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_Y);
2338 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH);
2339 rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT);
2340 return;
2343 rMark.SetMarking(false);
2345 SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
2347 if (mrViewData.IsFillMode() ||
2348 ( mrViewData.GetFillMode() == ScFillMode::MATRIX && rMEvt.IsMod1() ))
2350 nScFillModeMouseModifier = rMEvt.GetModifier();
2351 SCCOL nStartCol;
2352 SCROW nStartRow;
2353 SCCOL nEndCol;
2354 SCROW nEndRow;
2355 mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2356 ScRange aDelRange;
2357 bool bIsDel = mrViewData.GetDelMark( aDelRange );
2359 ScViewFunc* pView = mrViewData.GetView();
2360 pView->StopRefMode();
2361 mrViewData.ResetFillMode();
2362 pView->GetFunctionSet().SetAnchorFlag( false ); // #i5819# don't use AutoFill anchor flag for selection
2364 if ( bIsDel )
2366 pView->MarkRange( aDelRange, false );
2367 pView->DeleteContents( InsertDeleteFlags::CONTENTS );
2368 SCTAB nTab = mrViewData.GetTabNo();
2369 ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
2370 if ( aBlockRange != aDelRange )
2372 if ( aDelRange.aStart.Row() == nStartRow )
2373 aBlockRange.aEnd.SetCol( aDelRange.aStart.Col() - 1 );
2374 else
2375 aBlockRange.aEnd.SetRow( aDelRange.aStart.Row() - 1 );
2376 pView->MarkRange( aBlockRange, false );
2379 else
2381 mrViewData.GetDispatcher().Execute( FID_FILL_AUTO, SfxCallMode::SLOT | SfxCallMode::RECORD );
2383 if (comphelper::LibreOfficeKit::isActive())
2385 // prepare AutoFill menu items for "Copy Cells" and "Fill Series"
2386 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2387 boost::property_tree::ptree aMenu;
2388 boost::property_tree::ptree aItemTree;
2390 aItemTree.put("text", "~Copy Cells");
2391 aItemTree.put("type", "command");
2392 aItemTree.put("command", ".uno:AutoFill?Copy:bool=true");
2393 aItemTree.put("enabled", "true");
2394 aMenu.push_back(std::make_pair("", aItemTree));
2396 aItemTree.put("text", "~Fill Series");
2397 aItemTree.put("type", "command");
2398 aItemTree.put("command", ".uno:AutoFill?Copy:bool=false");
2399 aItemTree.put("enabled", "true");
2400 aMenu.push_back(std::make_pair("", aItemTree));
2401 aItemTree.clear();
2403 boost::property_tree::ptree aRoot;
2404 aRoot.add_child("menu", aMenu);
2406 std::stringstream aStream;
2407 boost::property_tree::write_json(aStream, aRoot, true);
2409 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU,
2410 OString(aStream.str()));
2414 else if (mrViewData.GetFillMode() == ScFillMode::MATRIX)
2416 SCTAB nTab = mrViewData.GetTabNo();
2417 SCCOL nStartCol;
2418 SCROW nStartRow;
2419 SCCOL nEndCol;
2420 SCROW nEndRow;
2421 mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2422 ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
2423 SCCOL nFillCol = mrViewData.GetRefEndX();
2424 SCROW nFillRow = mrViewData.GetRefEndY();
2425 ScAddress aEndPos( nFillCol, nFillRow, nTab );
2427 ScTabView* pView = mrViewData.GetView();
2428 pView->StopRefMode();
2429 mrViewData.ResetFillMode();
2430 pView->GetFunctionSet().SetAnchorFlag( false );
2432 if ( aEndPos != aBlockRange.aEnd )
2434 mrViewData.GetDocShell()->GetDocFunc().ResizeMatrix( aBlockRange, aEndPos );
2435 mrViewData.GetView()->MarkRange( ScRange( aBlockRange.aStart, aEndPos ) );
2438 else if (mrViewData.IsAnyFillMode())
2440 // Embedded area has been changed
2441 ScTabView* pView = mrViewData.GetView();
2442 pView->StopRefMode();
2443 mrViewData.ResetFillMode();
2444 pView->GetFunctionSet().SetAnchorFlag( false );
2445 mrViewData.GetDocShell()->UpdateOle(mrViewData);
2448 bool bRefMode = mrViewData.IsRefMode();
2449 if (bRefMode)
2450 pScMod->EndReference();
2452 // Format paintbrush mode (Switch)
2454 if (pScMod->GetIsWaterCan())
2456 // Check on undo already done above
2458 ScStyleSheetPool* pStylePool = mrViewData.GetDocument().
2459 GetStyleSheetPool();
2460 if ( pStylePool )
2462 SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(
2463 pStylePool->GetActualStyleSheet());
2465 if ( pStyleSheet )
2467 SfxStyleFamily eFamily = pStyleSheet->GetFamily();
2469 switch ( eFamily )
2471 case SfxStyleFamily::Para:
2472 mrViewData.GetView()->SetStyleSheetToMarked( pStyleSheet );
2473 mrViewData.GetView()->DoneBlockMode();
2474 break;
2476 case SfxStyleFamily::Page:
2477 mrViewData.GetDocument().SetPageStyle( mrViewData.GetTabNo(),
2478 pStyleSheet->GetName() );
2480 ScPrintFunc( mrViewData.GetDocShell(),
2481 mrViewData.GetViewShell()->GetPrinter(true),
2482 mrViewData.GetTabNo() ).UpdatePages();
2484 rBindings.Invalidate( SID_STATUS_PAGESTYLE );
2485 break;
2487 default:
2488 break;
2494 ScDBFunc* pView = mrViewData.GetView();
2495 ScDocument* pBrushDoc = pView->GetBrushDocument();
2496 if ( pBrushDoc )
2498 pView->PasteFromClip( InsertDeleteFlags::ATTRIB, pBrushDoc );
2499 if ( !pView->IsPaintBrushLocked() )
2500 pView->ResetBrushDocument(); // invalidates pBrushDoc pointer
2503 Point aPos = rMEvt.GetPosPixel();
2504 SCCOL nPosX;
2505 SCROW nPosY;
2506 SCTAB nTab = mrViewData.GetTabNo();
2507 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2508 ScDPObject* pDPObj = rDoc.GetDPAtCursor( nPosX, nPosY, nTab );
2510 // double click (only left button)
2512 bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
2513 if ( (rMEvt.GetClicks() == 2 && rMEvt.IsLeft())
2514 && !bRefMode
2515 && (nMouseStatus == SC_GM_DBLDOWN || (bIsTiledRendering && nMouseStatus != SC_GM_URLDOWN))
2516 && !pScMod->IsRefDialogOpen())
2518 // data pilot table
2519 if ( pDPObj && pDPObj->GetSaveData()->GetDrillDown() )
2521 ScAddress aCellPos( nPosX, nPosY, mrViewData.GetTabNo() );
2523 // Check for header drill-down first.
2524 sheet::DataPilotTableHeaderData aData;
2525 pDPObj->GetHeaderPositionData(aCellPos, aData);
2527 if ( ( aData.Flags & sheet::MemberResultFlags::HASMEMBER ) &&
2528 ! ( aData.Flags & sheet::MemberResultFlags::SUBTOTAL ) )
2530 css::sheet::DataPilotFieldOrientation nDummy;
2531 if ( pView->HasSelectionForDrillDown( nDummy ) )
2533 // execute slot to show dialog
2534 mrViewData.GetDispatcher().Execute( SID_OUTLINE_SHOW, SfxCallMode::SLOT | SfxCallMode::RECORD );
2536 else
2538 // toggle single entry
2539 ScDPObject aNewObj( *pDPObj );
2540 pDPObj->ToggleDetails( aData, &aNewObj );
2541 ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
2542 aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
2543 mrViewData.GetView()->CursorPosChanged(); // shells may be switched
2546 else
2548 // Check if the data area is double-clicked.
2550 Sequence<sheet::DataPilotFieldFilter> aFilters;
2551 if ( pDPObj->GetDataFieldPositionData(aCellPos, aFilters) )
2552 mrViewData.GetView()->ShowDataPilotSourceData( *pDPObj, aFilters );
2555 return;
2558 // Check for cell protection attribute.
2559 const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
2560 bool bEditAllowed = true;
2561 if ( pProtect && pProtect->isProtected() )
2563 bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
2564 bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
2565 bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
2567 if ( bSkipProtected && bSkipUnprotected )
2568 bEditAllowed = false;
2569 else if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
2570 bEditAllowed = false;
2573 if ( bEditAllowed )
2575 // edit cell contents
2576 mrViewData.GetViewShell()->UpdateInputHandler();
2577 pScMod->SetInputMode( SC_INPUT_TABLE );
2578 if (mrViewData.HasEditView(eWhich))
2580 // Set text cursor where clicked
2581 EditView* pEditView = mrViewData.GetEditView( eWhich );
2582 MouseEvent aEditEvt( rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 );
2583 pEditView->MouseButtonDown( aEditEvt );
2584 pEditView->MouseButtonUp( aEditEvt );
2588 if ( bIsTiledRendering && rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt ) )
2590 mrViewData.GetView()->SelectionChanged();
2593 return;
2596 // Links in edit cells
2598 bool bAlt = rMEvt.IsMod2();
2599 if ( !bAlt && !bRefMode && nMouseStatus == SC_GM_URLDOWN )
2601 // Only execute on ButtonUp, if ButtonDown also was done on a URL
2603 OUString aName, aUrl, aTarget;
2604 SCCOL nUrlCellX;
2605 if (GetEditUrl(rMEvt.GetPosPixel(), &aName, &aUrl, &aTarget, &nUrlCellX))
2607 nMouseStatus = SC_GM_NONE; // Ignore double-click
2608 bool isTiledRendering = comphelper::LibreOfficeKit::isActive();
2609 // ScGlobal::OpenURL() only understands Calc A1 style syntax.
2610 // Convert it to Calc A1 before calling OpenURL().
2611 if (rDoc.GetAddressConvention() == formula::FormulaGrammar::CONV_OOO)
2613 if (aUrl.startsWith("#")) {
2614 ScGlobal::OpenURL(aUrl, aTarget, isTiledRendering);
2615 return;
2617 // On a mobile device view there is no ctrl+click and for hyperlink popup
2618 // the cell coordinates must be sent along with click position for elegance
2619 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2620 if (isTiledRendering && pViewShell &&
2621 (pViewShell->isLOKMobilePhone() || pViewShell->isLOKTablet()))
2623 aPos = rMEvt.GetPosPixel();
2624 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2625 OString aCursor
2626 = pViewShell->GetViewData().describeCellCursorAt(nUrlCellX, nPosY);
2627 double fPPTX = pViewShell->GetViewData().GetPPTX();
2628 int mouseX = aPos.X() / fPPTX;
2629 int mouseY = aPos.Y() / fPPTX;
2630 OString aMsg(aUrl.toUtf8() + " coordinates: " + aCursor + ", "
2631 + OString::number(mouseX) + ", " + OString::number(mouseY));
2632 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
2633 } else
2634 ScGlobal::OpenURL(aUrl, aTarget);
2636 else
2638 ScAddress aTempAddr;
2639 ScAddress::ExternalInfo aExtInfo;
2640 ScRefFlags nRes = aTempAddr.Parse(aUrl, rDoc, rDoc.GetAddressConvention(), &aExtInfo);
2641 if (!(nRes & ScRefFlags::VALID))
2643 // Not a reference string. Pass it through unmodified.
2644 ScGlobal::OpenURL(aUrl, aTarget);
2645 return;
2648 OUStringBuffer aBuf;
2649 if (aExtInfo.mbExternal)
2651 // External reference.
2652 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
2653 const OUString* pStr = pRefMgr->getExternalFileName(aExtInfo.mnFileId);
2654 if (pStr)
2655 aBuf.append(*pStr);
2657 OUString aRefCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS, nullptr, formula::FormulaGrammar::CONV_OOO));
2658 aBuf.append("#" + aExtInfo.maTabName + "." + aRefCalcA1);
2659 ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget);
2661 else
2663 // Internal reference.
2664 OUString aUrlCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, formula::FormulaGrammar::CONV_OOO));
2665 aBuf.append("#" + aUrlCalcA1);
2666 ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget, isTiledRendering);
2670 // fire worksheet_followhyperlink event
2671 uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = rDoc.GetVbaEventProcessor();
2672 if( xVbaEvents.is() ) try
2674 aPos = rMEvt.GetPosPixel();
2675 nTab = mrViewData.GetTabNo();
2676 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2677 OUString sURL;
2678 ScRefCellValue aCell;
2679 if (lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
2681 ScAddress aCellPos( nPosX, nPosY, nTab );
2682 uno::Reference< table::XCell > xCell( new ScCellObj( mrViewData.GetDocShell(), aCellPos ) );
2683 uno::Sequence< uno::Any > aArgs{ uno::Any(xCell) };
2684 xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_FOLLOWHYPERLINK, aArgs );
2687 catch( uno::Exception& )
2691 return;
2695 // Gridwin - SelectionEngine
2697 // SelMouseButtonDown is called only for left button, but SelMouseButtonUp would return
2698 // sal_True for any call, so IsLeft must be checked here, too.
2700 if ( !(rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt )) )
2701 return;
2703 mrViewData.GetView()->SelectionChanged();
2705 SfxDispatcher* pDisp = mrViewData.GetViewShell()->GetDispatcher();
2706 bool bFormulaMode = pScMod->IsFormulaMode();
2707 OSL_ENSURE( pDisp || bFormulaMode, "Cursor moved on inactive View ?" );
2709 // #i14927# execute SID_CURRENTCELL (for macro recording) only if there is no
2710 // multiple selection, so the argument string completely describes the selection,
2711 // and executing the slot won't change the existing selection (executing the slot
2712 // here and from a recorded macro is treated equally)
2713 if ( pDisp && !bFormulaMode && !rMark.IsMultiMarked() )
2715 OUString aAddr; // CurrentCell
2716 if( rMark.IsMarked() )
2718 const ScRange& aScRange = rMark.GetMarkArea();
2719 aAddr = aScRange.Format(rDoc, ScRefFlags::RANGE_ABS);
2720 if ( aScRange.aStart == aScRange.aEnd )
2722 // make sure there is a range selection string even for a single cell
2723 aAddr += ":" + aAddr;
2726 //! SID_MARKAREA does not exist anymore ???
2727 //! What happens when selecting with the cursor ???
2729 else // only move cursor
2731 ScAddress aScAddress( mrViewData.GetCurX(), mrViewData.GetCurY(), 0 );
2732 aAddr = aScAddress.Format(ScRefFlags::ADDR_ABS);
2735 SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
2736 // We don't want to align to the cursor position because if the
2737 // cell cursor isn't visible after making selection, it would jump
2738 // back to the origin of the selection where the cell cursor is.
2739 SfxBoolItem aAlignCursorItem( FN_PARAM_2, false );
2740 pDisp->ExecuteList(SID_CURRENTCELL,
2741 SfxCallMode::SLOT | SfxCallMode::RECORD,
2742 { &aPosItem, &aAlignCursorItem });
2744 mrViewData.GetView()->InvalidateAttribs();
2747 mrViewData.GetViewShell()->SelectionChanged();
2749 if (bIsTiledRendering && !bRefMode)
2751 OUString aName, aUrl, aTarget;
2752 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2753 if (pViewShell && nPosX == m_nDownPosX && nPosY == m_nDownPosY
2754 && GetEditUrl(aPos, &aName, &aUrl, &aTarget, &nPosX))
2756 OString aMsg(aUrl.toUtf8() + " coordinates: "
2757 + pViewShell->GetViewData().describeCellCursorAt(nPosX, nPosY) + ", "
2758 + OString::number(aPos.X() / pViewShell->GetViewData().GetPPTX()) + ", "
2759 + OString::number(aPos.Y() / pViewShell->GetViewData().GetPPTY()));
2760 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
2764 m_nDownPosX = m_nDownPosY = -1;
2766 return;
2769 void ScGridWindow::FakeButtonUp()
2771 if ( nButtonDown )
2773 MouseEvent aEvent( aCurMousePos ); // nButtons = 0 -> ignore
2774 MouseButtonUp( aEvent );
2778 void ScGridWindow::MouseMove( const MouseEvent& rMEvt )
2780 aCurMousePos = rMEvt.GetPosPixel();
2782 if (rMEvt.IsLeaveWindow() && mpNoteMarker && !mpNoteMarker->IsByKeyboard())
2783 HideNoteMarker();
2785 ScModule* pScMod = ScModule::get();
2786 if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
2787 return;
2789 // If the Drag&Drop is started in the edit mode then sadly nothing else is kept
2790 if (bEEMouse && nButtonDown && !rMEvt.GetButtons())
2792 bEEMouse = false;
2793 nButtonDown = 0;
2794 nMouseStatus = SC_GM_NONE;
2795 return;
2798 if (nMouseStatus == SC_GM_IGNORE)
2799 return;
2801 if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode -> only what for Up
2802 return;
2804 if ( mrViewData.GetViewShell()->IsAuditShell() ) // Detective Fill Mode
2806 SetPointer( PointerStyle::Fill );
2807 return;
2810 bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
2812 if (bEEMouse && mrViewData.HasEditView( eWhich ))
2814 EditView* pEditView;
2815 SCCOL nEditCol;
2816 SCROW nEditRow;
2817 mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2819 if (comphelper::LibreOfficeKit::isActive() && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
2821 Point aMouse = rMEvt.GetPosPixel();
2822 tools::Rectangle aOutputArea = pEditView->GetOutputArea();
2823 comphelper::ScopeGuard aOutputGuard(
2824 [pEditView, aOutputArea] {
2825 pEditView->SetOutputArea(aOutputArea);
2828 lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
2829 pEditView->SetOutputArea(aOutputArea);
2831 MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
2832 rMEvt.GetButtons(), rMEvt.GetModifier());
2834 pEditView->MouseMove( aEvent );
2836 else
2837 pEditView->MouseMove( rMEvt );
2838 return;
2841 if (bDPMouse)
2843 DPMouseMove( rMEvt );
2844 return;
2847 if (bRFMouse)
2849 RFMouseMove( rMEvt, false );
2850 return;
2853 if (nPagebreakMouse)
2855 PagebreakMove( rMEvt, false );
2856 return;
2859 // Show other mouse pointer?
2861 bool bEditMode = mrViewData.HasEditView(eWhich);
2863 //! Test if refMode dragging !!!
2864 if ( bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()) )
2866 Point aPos = rMEvt.GetPosPixel();
2867 SCCOL nPosX;
2868 SCROW nPosY;
2869 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2871 EditView* pEditView;
2872 SCCOL nEditCol;
2873 SCROW nEditRow;
2874 mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2875 SCCOL nEndCol = mrViewData.GetEditEndCol();
2876 SCROW nEndRow = mrViewData.GetEditEndRow();
2878 if ( nPosX >= nEditCol && nPosX <= nEndCol &&
2879 nPosY >= nEditRow && nPosY <= nEndRow )
2881 if ( !pEditView )
2883 SetPointer( PointerStyle::Text );
2884 return;
2887 const SvxFieldItem* pFld;
2888 if ( comphelper::LibreOfficeKit::isActive() )
2890 Point aLogicClick = pEditView->GetOutputDevice().PixelToLogic(aPos);
2891 pFld = pEditView->GetField( aLogicClick );
2893 else
2895 pFld = pEditView->GetFieldUnderMousePointer();
2897 // Field can only be URL field
2898 bool bAlt = rMEvt.IsMod2();
2899 if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() && pFld )
2900 SetPointer( PointerStyle::RefHand );
2901 else if (pEditView->getEditEngine().IsEffectivelyVertical())
2902 SetPointer( PointerStyle::TextVertical );
2903 else
2904 SetPointer( PointerStyle::Text );
2905 return;
2909 bool bWater = pScMod->GetIsWaterCan() || mrViewData.GetView()->HasPaintBrush();
2910 if (bWater)
2911 SetPointer( PointerStyle::Fill );
2913 if (!bWater)
2915 bool bCross = false;
2917 // range finder
2919 RfCorner rCorner = NONE;
2920 if ( HitRangeFinder( rMEvt.GetPosPixel(), rCorner, nullptr, nullptr, nullptr ) )
2922 if (rCorner != NONE)
2923 SetPointer( PointerStyle::Cross );
2924 else
2925 SetPointer( PointerStyle::Hand );
2926 bCross = true;
2929 // Page-Break-Mode
2931 if ( !nButtonDown && mrViewData.IsPagebreakMode() )
2933 sal_uInt16 nBreakType = HitPageBreak( rMEvt.GetPosPixel(), nullptr, nullptr, nullptr );
2934 if (nBreakType != 0 )
2936 PointerStyle eNew = PointerStyle::Arrow;
2937 switch ( nBreakType )
2939 case SC_PD_RANGE_L:
2940 case SC_PD_RANGE_R:
2941 case SC_PD_BREAK_H:
2942 eNew = PointerStyle::ESize;
2943 break;
2944 case SC_PD_RANGE_T:
2945 case SC_PD_RANGE_B:
2946 case SC_PD_BREAK_V:
2947 eNew = PointerStyle::SSize;
2948 break;
2949 case SC_PD_RANGE_TL:
2950 case SC_PD_RANGE_BR:
2951 eNew = PointerStyle::SESize;
2952 break;
2953 case SC_PD_RANGE_TR:
2954 case SC_PD_RANGE_BL:
2955 eNew = PointerStyle::NESize;
2956 break;
2958 SetPointer( eNew );
2959 bCross = true;
2963 // Show fill cursor?
2965 if ( !bFormulaMode && !nButtonDown )
2966 if (TestMouse( rMEvt, false ))
2967 bCross = true;
2969 if ( nButtonDown && mrViewData.IsAnyFillMode() )
2971 SetPointer( PointerStyle::Cross );
2972 bCross = true;
2973 nScFillModeMouseModifier = rMEvt.GetModifier(); // evaluated for AutoFill and Matrix
2976 if (!bCross)
2978 bool bAlt = rMEvt.IsMod2();
2980 if (bEditMode) // First has to be in edit mode!
2981 SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
2982 else if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() &&
2983 GetEditUrl(rMEvt.GetPosPixel()) )
2984 SetPointer( PointerStyle::RefHand );
2985 else if ( DrawMouseMove(rMEvt) ) // Reset pointer
2986 return;
2990 // In LOK case, avoid spurious "leavingwindow" mouse move events which has negative coordinates.
2991 // Such events occur for some reason when a user is selecting a range, (even when not leaving the view area)
2992 // with one or more other viewers in that sheet.
2993 bool bSkipSelectionUpdate = comphelper::LibreOfficeKit::isActive() &&
2994 rMEvt.IsLeaveWindow() && (aCurMousePos.X() < 0 || aCurMousePos.Y() < 0);
2996 if (!bSkipSelectionUpdate)
2997 mrViewData.GetView()->GetSelEngine()->SelMouseMove( rMEvt );
3000 static void lcl_InitMouseEvent(css::awt::MouseEvent& rEvent, const MouseEvent& rEvt)
3002 rEvent.Modifiers = 0;
3003 if ( rEvt.IsShift() )
3004 rEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
3005 if ( rEvt.IsMod1() )
3006 rEvent.Modifiers |= css::awt::KeyModifier::MOD1;
3007 if ( rEvt.IsMod2() )
3008 rEvent.Modifiers |= css::awt::KeyModifier::MOD2;
3009 if ( rEvt.IsMod3() )
3010 rEvent.Modifiers |= css::awt::KeyModifier::MOD3;
3012 rEvent.Buttons = 0;
3013 if ( rEvt.IsLeft() )
3014 rEvent.Buttons |= css::awt::MouseButton::LEFT;
3015 if ( rEvt.IsRight() )
3016 rEvent.Buttons |= css::awt::MouseButton::RIGHT;
3017 if ( rEvt.IsMiddle() )
3018 rEvent.Buttons |= css::awt::MouseButton::MIDDLE;
3020 rEvent.X = rEvt.GetPosPixel().X();
3021 rEvent.Y = rEvt.GetPosPixel().Y();
3022 rEvent.ClickCount = rEvt.GetClicks();
3023 rEvent.PopupTrigger = false;
3026 bool ScGridWindow::PreNotify( NotifyEvent& rNEvt )
3028 bool bDone = false;
3029 NotifyEventType nType = rNEvt.GetType();
3030 if ( nType == NotifyEventType::MOUSEBUTTONUP || nType == NotifyEventType::MOUSEBUTTONDOWN )
3032 vcl::Window* pWindow = rNEvt.GetWindow();
3033 if (pWindow == this)
3035 SfxViewFrame& rViewFrame = mrViewData.GetViewShell()->GetViewFrame();
3036 css::uno::Reference<css::frame::XController> xController = rViewFrame.GetFrame().GetController();
3037 if (xController.is())
3039 ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
3040 if (pImp && pImp->IsMouseListening())
3042 css::awt::MouseEvent aEvent;
3043 lcl_InitMouseEvent( aEvent, *rNEvt.GetMouseEvent() );
3044 if ( rNEvt.GetWindow() )
3045 aEvent.Source = rNEvt.GetWindow()->GetComponentInterface();
3046 if ( nType == NotifyEventType::MOUSEBUTTONDOWN)
3047 bDone = pImp->MousePressed( aEvent );
3048 else
3049 bDone = pImp->MouseReleased( aEvent );
3054 if (bDone) // event consumed by a listener
3056 if ( nType == NotifyEventType::MOUSEBUTTONDOWN )
3058 const MouseEvent* pMouseEvent = rNEvt.GetMouseEvent();
3059 if ( pMouseEvent->IsRight() && pMouseEvent->GetClicks() == 1 )
3061 // If a listener returned true for a right-click call, also prevent opening the context menu
3062 // (this works only if the context menu is opened on mouse-down)
3063 nMouseStatus = SC_GM_IGNORE;
3067 return true;
3069 else
3070 return Window::PreNotify( rNEvt );
3073 void ScGridWindow::Tracking( const TrackingEvent& rTEvt )
3075 // Since the SelectionEngine does not track, the events have to be
3076 // handed to the different MouseHandler...
3078 const MouseEvent& rMEvt = rTEvt.GetMouseEvent();
3080 if ( rTEvt.IsTrackingCanceled() ) // Cancel everything...
3082 if (!mrViewData.GetView()->IsInActivatePart() && !ScModule::get()->IsRefDialogOpen())
3084 if (bDPMouse)
3085 bDPMouse = false; // Paint for each bDragRect
3086 if (bDragRect)
3088 bDragRect = false;
3089 UpdateDragRectOverlay();
3091 if (bRFMouse)
3093 RFMouseMove( rMEvt, true ); // Not possible to cancel properly...
3094 bRFMouse = false;
3096 if (nPagebreakMouse)
3098 bPagebreakDrawn = false;
3099 UpdateDragRectOverlay();
3100 nPagebreakMouse = SC_PD_NONE;
3103 SetPointer( PointerStyle::Arrow );
3104 StopMarking();
3105 MouseButtonUp( rMEvt ); // With status SC_GM_IGNORE from StopMarking
3107 bool bRefMode = mrViewData.IsRefMode();
3108 if (bRefMode)
3109 ScModule::get()->EndReference(); // Do not let the Dialog remain minimized
3112 else if ( rTEvt.IsTrackingEnded() )
3114 if (!comphelper::LibreOfficeKit::isActive())
3116 // MouseButtonUp always with matching buttons (eg for test tool, # 63148 #)
3117 // The tracking event will indicate if it was completed and not canceled.
3118 MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
3119 rMEvt.GetMode(), nButtonDown, rMEvt.GetModifier() );
3120 MouseButtonUp( aUpEvt );
3123 else
3124 MouseMove( rMEvt );
3127 void ScGridWindow::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
3129 if (mpFilterBox || nPagebreakMouse)
3130 return;
3132 HideNoteMarker();
3134 CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
3136 if (bEEMouse && mrViewData.HasEditView( eWhich ))
3138 EditView* pEditView;
3139 SCCOL nEditCol;
3140 SCROW nEditRow;
3141 mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
3143 // don't remove the edit view while switching views
3144 ScModule* pScMod = ScModule::get();
3145 pScMod->SetInEditCommand( true );
3147 pEditView->Command( aDragEvent );
3149 ScInputHandler* pHdl = pScMod->GetInputHdl();
3150 if (pHdl)
3151 pHdl->DataChanged();
3153 pScMod->SetInEditCommand( false );
3154 if (!mrViewData.IsActive()) // dropped to different view?
3156 ScInputHandler* pViewHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
3157 if ( pViewHdl && mrViewData.HasEditView( eWhich ) )
3159 pViewHdl->CancelHandler();
3160 ShowCursor(); // missing from KillEditView
3164 else
3165 if ( !DrawCommand(aDragEvent) )
3166 mrViewData.GetView()->GetSelEngine()->Command( aDragEvent );
3169 static void lcl_SetTextCursorPos( ScViewData& rViewData, ScSplitPos eWhich, vcl::Window* pWin )
3171 SCCOL nCol = rViewData.GetCurX();
3172 SCROW nRow = rViewData.GetCurY();
3173 tools::Rectangle aEditArea = rViewData.GetEditArea( eWhich, nCol, nRow, pWin, nullptr, true );
3174 aEditArea.SetRight( aEditArea.Left() );
3175 aEditArea = pWin->PixelToLogic( aEditArea );
3176 pWin->SetCursorRect( &aEditArea );
3179 void ScGridWindow::Command( const CommandEvent& rCEvt )
3181 // The command event is send to the window after a possible context
3182 // menu from an inplace client is closed. Now we have the chance to
3183 // deactivate the inplace client without any problem regarding parent
3184 // windows and code on the stack.
3185 CommandEventId nCmd = rCEvt.GetCommand();
3186 ScTabViewShell* pTabViewSh = mrViewData.GetViewShell();
3187 SfxInPlaceClient* pClient = pTabViewSh->GetIPClient();
3188 if ( pClient &&
3189 pClient->IsObjectInPlaceActive() &&
3190 nCmd == CommandEventId::ContextMenu )
3192 pTabViewSh->DeactivateOle();
3193 return;
3196 ScModule* pScMod = ScModule::get();
3197 OSL_ENSURE( nCmd != CommandEventId::StartDrag, "ScGridWindow::Command called with CommandEventId::StartDrag" );
3199 if (nCmd == CommandEventId::ModKeyChange)
3201 Window::Command(rCEvt);
3202 return;
3205 if ( nCmd == CommandEventId::StartExtTextInput ||
3206 nCmd == CommandEventId::EndExtTextInput ||
3207 nCmd == CommandEventId::ExtTextInput ||
3208 nCmd == CommandEventId::CursorPos ||
3209 nCmd == CommandEventId::QueryCharPosition )
3211 bool bEditView = mrViewData.HasEditView( eWhich );
3212 if (!bEditView)
3214 // only if no cell editview is active, look at drawview
3215 SdrView* pSdrView = mrViewData.GetView()->GetScDrawView();
3216 if ( pSdrView )
3218 OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3219 if ( pOlView && pOlView->GetWindow() == this )
3221 pOlView->Command( rCEvt );
3222 return; // done
3227 if ( nCmd == CommandEventId::CursorPos && !bEditView )
3229 // CURSORPOS may be called without following text input,
3230 // to set the input method window position
3231 // -> input mode must not be started,
3232 // manually calculate text insert position if not in input mode
3234 lcl_SetTextCursorPos( mrViewData, eWhich, this );
3235 return;
3238 ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
3239 if ( pHdl )
3241 pHdl->InputCommand( rCEvt );
3242 return; // done
3245 Window::Command( rCEvt );
3246 return;
3249 if ( nCmd == CommandEventId::PasteSelection )
3251 if ( bEEMouse )
3253 // EditEngine handles selection in MouseButtonUp - no action
3254 // needed in command handler
3256 else
3258 PasteSelection( rCEvt.GetMousePosPixel() );
3260 return;
3263 if ( nCmd == CommandEventId::InputLanguageChange )
3265 // #i55929# Font and font size state depends on input language if nothing is selected,
3266 // so the slots have to be invalidated when the input language is changed.
3268 SfxBindings& rBindings = mrViewData.GetBindings();
3269 rBindings.Invalidate( SID_ATTR_CHAR_FONT );
3270 rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
3271 return;
3274 if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
3276 bool bDone = mrViewData.GetView()->ScrollCommand( rCEvt, eWhich );
3277 if (!bDone)
3278 Window::Command(rCEvt);
3279 return;
3282 if (nCmd == CommandEventId::GesturePan)
3284 bool bDone = mrViewData.GetView()->GesturePanCommand(rCEvt);
3285 if (!bDone)
3286 Window::Command(rCEvt);
3287 return;
3290 if (nCmd == CommandEventId::GestureZoom)
3292 bool bDone = mrViewData.GetView()->GestureZoomCommand(rCEvt);
3293 if (!bDone)
3294 Window::Command(rCEvt);
3295 return;
3298 // #i7560# FormulaMode check is below scrolling - scrolling is allowed during formula input
3299 bool bDisable = pScMod->IsFormulaMode() ||
3300 pScMod->IsModalMode(mrViewData.GetSfxDocShell());
3301 if (bDisable)
3302 return;
3304 if (nCmd != CommandEventId::ContextMenu || pScMod->GetIsWaterCan())
3305 return;
3307 bool bMouse = rCEvt.IsMouseEvent();
3308 if ( bMouse && nMouseStatus == SC_GM_IGNORE )
3309 return;
3311 if (mrViewData.IsAnyFillMode())
3313 mrViewData.GetView()->StopRefMode();
3314 mrViewData.ResetFillMode();
3316 ReleaseMouse();
3317 StopMarking();
3319 Point aPosPixel = rCEvt.GetMousePosPixel();
3320 Point aMenuPos = aPosPixel;
3322 bool bPosIsInEditView = mrViewData.HasEditView(eWhich);
3323 SCCOL nCellX = -1;
3324 SCROW nCellY = -1;
3325 mrViewData.GetPosFromPixel(aPosPixel.X(), aPosPixel.Y(), eWhich, nCellX, nCellY);
3326 // GetPosFromPixel ignores the fact that when editing a cell, the cell might grow to cover
3327 // other rows/columns. In addition, the mouse might now be outside the edited cell.
3328 if (bPosIsInEditView)
3330 if (nCellX >= mrViewData.GetEditViewCol() && nCellX <= mrViewData.GetEditEndCol())
3331 nCellX = mrViewData.GetEditViewCol();
3332 else
3333 bPosIsInEditView = false;
3335 if (nCellY >= mrViewData.GetEditViewRow() && nCellY <= mrViewData.GetEditEndRow())
3336 nCellY = mrViewData.GetEditViewRow();
3337 else
3338 bPosIsInEditView = false;
3340 if (!bPosIsInEditView)
3342 // Close the edit view when moving outside of the edited cell
3343 // to avoid showing the edit popup, or providing the wrong EditView to spellcheck.
3344 ScInputHandler* pHdl = pScMod->GetInputHdl();
3345 if (pHdl)
3346 pHdl->EnterHandler();
3350 bool bSpellError = false;
3351 SCCOL nColSpellError = nCellX;
3353 if ( bMouse )
3355 ScDocument& rDoc = mrViewData.GetDocument();
3356 SCTAB nTab = mrViewData.GetTabNo();
3357 const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
3358 bool bSelectAllowed = true;
3359 if ( pProtect && pProtect->isProtected() )
3361 // This sheet is protected. Check if a context menu is allowed on this cell.
3362 bool bCellProtected = rDoc.HasAttrib(nCellX, nCellY, nTab, nCellX, nCellY, nTab, HasAttrFlags::Protected);
3363 bool bSelProtected = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
3364 bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
3366 if (bCellProtected)
3367 bSelectAllowed = bSelProtected;
3368 else
3369 bSelectAllowed = bSelUnprotected;
3371 if (!bSelectAllowed)
3372 // Selecting this cell is not allowed, neither is context menu.
3373 return;
3375 if (mpSpellCheckCxt)
3377 // Find the first string to the left for spell checking in case the current cell is empty.
3378 ScAddress aPos(nCellX, nCellY, nTab);
3379 ScRefCellValue aSpellCheckCell(rDoc, aPos);
3380 while (!bPosIsInEditView && aSpellCheckCell.getType() == CELLTYPE_NONE)
3382 // Loop until we get the first non-empty cell in the row.
3383 aPos.IncCol(-1);
3384 if (aPos.Col() < 0)
3385 break;
3387 aSpellCheckCell.assign(rDoc, aPos);
3390 if (aPos.Col() >= 0 && (aSpellCheckCell.getType() == CELLTYPE_STRING || aSpellCheckCell.getType() == CELLTYPE_EDIT))
3391 nColSpellError = aPos.Col();
3393 // Is there possibly a misspelled word somewhere in the cell?
3394 // A "yes" does not mean that the word under the mouse pointer is wrong though.
3395 bSpellError = (mpSpellCheckCxt->isMisspelled(nColSpellError, nCellY));
3396 // Avoid situations where selecting the cell-with-wrong-spelling would be bad
3397 if (bSpellError)
3399 // When the mouse is over an empty cell, text with spelling errors
3400 // potentially could have overflowed underneath the mouse pointer
3401 if (nColSpellError != nCellX)
3403 // If the mouse is over a selected cell, only consider spell-checking
3404 // if the cell with the misspelling is also selected. tdf#157038
3405 if (mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY))
3406 bSpellError = mrViewData.GetMarkData().IsCellMarked(nColSpellError, nCellY);
3411 // #i18735# First select the item under the mouse pointer.
3412 // This can change the selection, and the view state (edit mode, etc).
3413 SelectForContextMenu(aPosPixel, bSpellError ? nColSpellError : nCellX, nCellY);
3416 bool bDone = false;
3417 bool bEdit = mrViewData.HasEditView(eWhich);
3419 if ( !bEdit )
3421 // Edit cell with spelling errors?
3422 // tdf#127341 the formerly used GetEditUrl(aPosPixel) additionally
3423 // to bSpellError activated EditMode here for right-click on URL
3424 // which prevents the regular context-menu from appearing. Since this
3425 // is more expected than the context-menu for editing an URL, I removed
3426 // this. If this was wanted and can be argued it might be re-activated.
3427 // For now, reduce to spelling errors - as the original comment above
3428 // suggests.
3429 if (bMouse && bSpellError)
3431 // GetEditUrlOrError has already moved the Cursor
3433 pScMod->SetInputMode( SC_INPUT_TABLE );
3434 bEdit = mrViewData.HasEditView(eWhich); // Did it work?
3436 OSL_ENSURE( bEdit, "Can not be switched in edit mode" );
3439 if ( bEdit )
3441 EditView* pEditView = mrViewData.GetEditView( eWhich ); // is then not 0
3443 if ( !bMouse )
3445 vcl::Cursor* pCur = pEditView->GetCursor();
3446 if ( pCur )
3448 Point aLogicPos = pCur->GetPos();
3449 // use the position right of the cursor (spell popup is opened if
3450 // the cursor is before the word, but not if behind it)
3451 aLogicPos.AdjustX(pCur->GetWidth() );
3452 aLogicPos.AdjustY(pCur->GetHeight() / 2 ); // center vertically
3453 aMenuPos = LogicToPixel( aLogicPos );
3457 // if edit mode was just started above, online spelling may be incomplete
3458 pEditView->getEditEngine().CompleteOnlineSpelling();
3460 // IsCursorAtWrongSpelledWord could be used for !bMouse
3461 // if there was a corresponding ExecuteSpellPopup call
3463 if (bSpellError)
3465 // On OS/2 when clicking next to the Popup menu, the MouseButtonDown
3466 // comes before the end of menu execute, thus the SetModified has to
3467 // be done prior to this (Bug #40968#)
3468 ScInputHandler* pHdl = pScMod->GetInputHdl();
3469 if (pHdl)
3470 pHdl->SetModified();
3472 const OUString sOldText = pHdl ? pHdl->GetEditString() : u""_ustr;
3474 // Only done/shown if a misspelled word is actually under the mouse pointer.
3475 Link<SpellCallbackInfo&,void> aLink = LINK( this, ScGridWindow, PopupSpellingHdl );
3476 bDone = pEditView->ExecuteSpellPopup(aMenuPos, aLink);
3478 // If the spelling is corrected, stop editing to flush any cached spelling info.
3479 // Or, if no misspelled word at this position, and it wasn't initially in edit mode,
3480 // then exit the edit mode in order to get the full context popup (not edit popup).
3481 if (pHdl && (pHdl->GetEditString() != sOldText
3482 || (!bDone && !bPosIsInEditView)))
3484 pHdl->EnterHandler();
3487 if (!bDone && nColSpellError != nCellX)
3489 // NOTE: This call can change the selection, and the view state (edit mode, etc).
3490 SelectForContextMenu(aPosPixel, nCellX, nCellY);
3494 else if ( !bMouse )
3496 // non-edit menu by keyboard -> use lower right of cell cursor position
3497 ScDocument& rDoc = mrViewData.GetDocument();
3498 SCTAB nTabNo = mrViewData.GetTabNo();
3499 bool bLayoutIsRTL = rDoc.IsLayoutRTL(nTabNo);
3501 SCCOL nCurX = mrViewData.GetCurX();
3502 SCROW nCurY = mrViewData.GetCurY();
3503 aMenuPos = mrViewData.GetScrPos( nCurX, nCurY, eWhich, true );
3504 tools::Long nSizeXPix;
3505 tools::Long nSizeYPix;
3506 mrViewData.GetMergeSizePixel( nCurX, nCurY, nSizeXPix, nSizeYPix );
3507 // fdo#55432 take the correct position for RTL sheet
3508 aMenuPos.AdjustX(bLayoutIsRTL ? -nSizeXPix : nSizeXPix );
3509 aMenuPos.AdjustY(nSizeYPix );
3511 ScTabViewShell* pViewSh = mrViewData.GetViewShell();
3512 if (pViewSh)
3514 // Is a draw object selected?
3516 SdrView* pDrawView = pViewSh->GetScDrawView();
3517 if (pDrawView && pDrawView->GetMarkedObjectList().GetMarkCount() != 0)
3519 // #100442#; the context menu should open in the middle of the selected objects
3520 tools::Rectangle aSelectRect(LogicToPixel(pDrawView->GetAllMarkedBoundRect()));
3521 aMenuPos = aSelectRect.Center();
3526 if (bDone)
3527 return;
3529 SfxDispatcher::ExecutePopup( this, &aMenuPos );
3532 void ScGridWindow::SelectForContextMenu( const Point& rPosPixel, SCCOL nCellX, SCROW nCellY )
3534 // #i18735# if the click was outside of the current selection,
3535 // the cursor is moved or an object at the click position selected.
3536 // (see SwEditWin::SelectMenuPosition in Writer)
3538 ScTabView* pView = mrViewData.GetView();
3539 ScDrawView* pDrawView = pView->GetScDrawView();
3541 // check cell edit mode
3543 if ( mrViewData.HasEditView(eWhich) )
3545 ScModule* pScMod = ScModule::get();
3546 SCCOL nEditStartCol = mrViewData.GetEditViewCol(); //! change to GetEditStartCol after calcrtl is integrated
3547 SCROW nEditStartRow = mrViewData.GetEditViewRow();
3548 SCCOL nEditEndCol = mrViewData.GetEditEndCol();
3549 SCROW nEditEndRow = mrViewData.GetEditEndRow();
3551 if ( nCellX >= nEditStartCol && nCellX <= nEditEndCol &&
3552 nCellY >= nEditStartRow && nCellY <= nEditEndRow )
3554 // handle selection within the EditView
3556 EditView* pEditView = mrViewData.GetEditView( eWhich ); // not NULL (HasEditView)
3557 EditEngine& rEditEngine = pEditView->getEditEngine();
3558 tools::Rectangle aOutputArea = pEditView->GetOutputArea();
3559 tools::Rectangle aVisArea = pEditView->GetVisArea();
3561 Point aTextPos = PixelToLogic( rPosPixel );
3562 if (rEditEngine.IsEffectivelyVertical()) // have to manually transform position
3564 aTextPos -= aOutputArea.TopRight();
3565 tools::Long nTemp = -aTextPos.X();
3566 aTextPos.setX( aTextPos.Y() );
3567 aTextPos.setY( nTemp );
3569 else
3570 aTextPos -= aOutputArea.TopLeft();
3571 aTextPos += aVisArea.TopLeft(); // position in the edit document
3573 ESelection aCompare(rEditEngine.FindDocPosition(aTextPos));
3574 ESelection aSelection = pEditView->GetSelection();
3575 aSelection.Adjust(); // needed for IsLess/IsGreater
3576 if ( aCompare < aSelection || aCompare > aSelection )
3578 // clicked outside the selected text - deselect and move text cursor
3579 MouseEvent aEvent( rPosPixel );
3580 pEditView->MouseButtonDown( aEvent );
3581 pEditView->MouseButtonUp( aEvent );
3582 pScMod->InputSelection( pEditView );
3585 return; // clicked within the edit view - keep edit mode
3587 else
3589 // outside of the edit view - end edit mode, regardless of cell selection, then continue
3590 pScMod->InputEnterHandler();
3594 // check draw text edit mode
3596 Point aLogicPos = PixelToLogic( rPosPixel ); // after cell edit mode is ended
3597 if ( pDrawView && pDrawView->GetTextEditObject() && pDrawView->GetTextEditOutlinerView() )
3599 OutlinerView* pOlView = pDrawView->GetTextEditOutlinerView();
3600 tools::Rectangle aOutputArea = pOlView->GetOutputArea();
3601 if ( aOutputArea.Contains( aLogicPos ) )
3603 // handle selection within the OutlinerView
3605 Outliner* pOutliner = pOlView->GetOutliner();
3606 const EditEngine& rEditEngine = pOutliner->GetEditEngine();
3607 tools::Rectangle aVisArea = pOlView->GetVisArea();
3609 Point aTextPos = aLogicPos;
3610 if ( pOutliner->IsVertical() ) // have to manually transform position
3612 aTextPos -= aOutputArea.TopRight();
3613 tools::Long nTemp = -aTextPos.X();
3614 aTextPos.setX( aTextPos.Y() );
3615 aTextPos.setY( nTemp );
3617 else
3618 aTextPos -= aOutputArea.TopLeft();
3619 aTextPos += aVisArea.TopLeft(); // position in the edit document
3621 ESelection aCompare(rEditEngine.FindDocPosition(aTextPos));
3622 ESelection aSelection = pOlView->GetSelection();
3623 aSelection.Adjust(); // needed for IsLess/IsGreater
3624 if ( aCompare < aSelection || aCompare > aSelection )
3626 // clicked outside the selected text - deselect and move text cursor
3627 // use DrawView to allow extra handling there (none currently)
3628 MouseEvent aEvent( rPosPixel );
3629 pDrawView->MouseButtonDown( aEvent, GetOutDev() );
3630 pDrawView->MouseButtonUp( aEvent, GetOutDev() );
3633 return; // clicked within the edit area - keep edit mode
3635 else
3637 // Outside of the edit area - end text edit mode, then continue.
3638 // DrawDeselectAll also ends text edit mode and updates the shells.
3639 // If the click was on the edited object, it will be selected again below.
3640 pView->DrawDeselectAll();
3644 // look for existing selection
3646 bool bHitSelected = false;
3647 if ( pDrawView && pDrawView->IsMarkedObjHit( aLogicPos ) )
3649 // clicked on selected object -> don't change anything
3650 bHitSelected = true;
3652 else if ( mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY) )
3654 // clicked on selected cell -> don't change anything
3655 bHitSelected = true;
3658 // select drawing object or move cell cursor
3660 if ( bHitSelected )
3661 return;
3663 bool bWasDraw = ( pDrawView && pDrawView->GetMarkedObjectList().GetMarkCount() != 0 );
3664 bool bHitDraw = false;
3665 if ( pDrawView )
3667 pDrawView->UnmarkAllObj();
3668 // Unlock the Internal Layer in order to activate the context menu.
3669 // re-lock in ScDrawView::MarkListHasChanged()
3670 lcl_UnLockComment(pDrawView, aLogicPos, mrViewData);
3671 bHitDraw = pDrawView->MarkObj( aLogicPos );
3672 // draw shell is activated in MarkListHasChanged
3674 if ( !bHitDraw )
3676 pView->Unmark();
3677 pView->SetCursor(nCellX, nCellY);
3678 if ( bWasDraw )
3679 mrViewData.GetViewShell()->SetDrawShell( false ); // switch shells
3683 void ScGridWindow::KeyInput(const KeyEvent& rKEvt)
3685 // Cursor control for ref input dialog
3686 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
3688 #ifdef DBG_UTIL
3690 if (rKeyCode.IsMod1() && rKeyCode.IsShift())
3692 if (rKeyCode.GetCode() == KEY_F12)
3694 dumpColumnInformationPixel();
3696 else if (rKeyCode.GetCode() == KEY_F11)
3698 dumpGraphicInformation();
3700 else if (rKeyCode.GetCode() == KEY_F10)
3702 dumpColumnInformationHmm();
3704 else if (rKeyCode.GetCode() == KEY_F6)
3706 dumpCellProperties();
3708 else if (rKeyCode.GetCode() == KEY_F8)
3710 dumpColumnCellStorage();
3712 else if (rKeyCode.GetCode() == KEY_F7)
3714 ScDocument& rDoc = mrViewData.GetDocument();
3715 auto& rMapper = rDoc.GetExternalDataMapper();
3716 for (auto& itr : rMapper.getDataSources())
3718 itr.refresh(&rDoc);
3720 return;
3724 #endif
3726 ScModule* mod = ScModule::get();
3727 if( mod->IsRefDialogOpen() )
3729 if( !rKeyCode.GetModifier() && (rKeyCode.GetCode() == KEY_F2) )
3731 mod->EndReference();
3733 else if( mrViewData.GetViewShell()->MoveCursorKeyInput( rKEvt ) )
3735 ScRange aRef(
3736 mrViewData.GetRefStartX(), mrViewData.GetRefStartY(), mrViewData.GetRefStartZ(),
3737 mrViewData.GetRefEndX(), mrViewData.GetRefEndY(), mrViewData.GetRefEndZ() );
3738 mod->SetReference(aRef, mrViewData.GetDocument());
3740 mrViewData.GetViewShell()->SelectionChanged();
3741 return ;
3743 else if( rKeyCode.GetCode() == KEY_RETURN && mrViewData.IsPasteMode()
3744 && mod->GetInputOptions().GetEnterPasteMode() )
3746 ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
3747 ScClipUtil::PasteFromClipboard( mrViewData, pTabViewShell, true );
3749 // Clear clipboard content.
3750 uno::Reference<datatransfer::clipboard::XClipboard> xSystemClipboard =
3751 GetClipboard();
3752 if (xSystemClipboard.is())
3754 xSystemClipboard->setContents(
3755 uno::Reference<datatransfer::XTransferable>(),
3756 uno::Reference<datatransfer::clipboard::XClipboardOwner>());
3759 // hide the border around the copy source
3760 mrViewData.SetPasteMode( ScPasteFlags::NONE );
3761 // Clear CopySourceOverlay in each window of a split/frozen tabview
3762 mrViewData.GetView()->UpdateCopySourceOverlay();
3763 return;
3765 // if semi-modeless SfxChildWindow dialog above, then no KeyInputs:
3766 else if( !mrViewData.IsAnyFillMode() )
3768 if (rKeyCode.GetCode() == KEY_ESCAPE)
3770 mrViewData.SetPasteMode( ScPasteFlags::NONE );
3771 // Clear CopySourceOverlay in each window of a split/frozen tabview
3772 mrViewData.GetView()->UpdateCopySourceOverlay();
3774 // query for existing note marker before calling ViewShell's keyboard handling
3775 // which may remove the marker
3776 bool bHadKeyMarker = mpNoteMarker && mpNoteMarker->IsByKeyboard();
3777 ScTabViewShell* pViewSh = mrViewData.GetViewShell();
3779 if (mrViewData.GetDocShell()->GetProgress())
3780 return;
3782 if (DrawKeyInput(rKEvt, this))
3784 const vcl::KeyCode& rLclKeyCode = rKEvt.GetKeyCode();
3785 if (rLclKeyCode.GetCode() == KEY_DOWN
3786 || rLclKeyCode.GetCode() == KEY_UP
3787 || rLclKeyCode.GetCode() == KEY_LEFT
3788 || rLclKeyCode.GetCode() == KEY_RIGHT)
3790 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
3791 SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
3792 rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
3793 rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
3795 return;
3798 if (!mrViewData.GetView()->IsDrawSelMode() && !DrawHasMarkedObj()) // No entries in draw mode
3799 { //! check DrawShell !!!
3800 if (pViewSh->TabKeyInput(rKEvt))
3801 return;
3803 else
3804 if (pViewSh->SfxViewShell::KeyInput(rKEvt)) // from SfxViewShell
3805 return;
3807 vcl::KeyCode aCode = rKEvt.GetKeyCode();
3808 if ( aCode.GetCode() == KEY_ESCAPE && aCode.GetModifier() == 0 )
3810 if ( bHadKeyMarker )
3811 HideNoteMarker();
3812 else
3813 pViewSh->Escape();
3814 return;
3816 if ( aCode.GetCode() == KEY_F1 && aCode.GetModifier() == KEY_MOD1 )
3818 // ctrl-F1 shows or hides the note or redlining info for the cursor position
3819 // (hard-coded because F1 can't be configured)
3821 if ( bHadKeyMarker )
3822 HideNoteMarker(); // hide when previously visible
3823 else
3824 ShowNoteMarker( mrViewData.GetCurX(), mrViewData.GetCurY(), true );
3825 return;
3827 if (aCode.GetCode() == KEY_BRACKETLEFT && aCode.GetModifier() == KEY_MOD1)
3829 pViewSh->DetectiveMarkPred();
3830 return;
3832 if (aCode.GetCode() == KEY_BRACKETRIGHT && aCode.GetModifier() == KEY_MOD1)
3834 pViewSh->DetectiveMarkSucc();
3835 return;
3840 Window::KeyInput(rKEvt);
3843 OUString ScGridWindow::GetSurroundingText() const
3845 bool bEditView = mrViewData.HasEditView(eWhich);
3846 if (bEditView)
3848 ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
3849 if (pHdl)
3850 return pHdl->GetSurroundingText();
3852 else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3854 // if no cell editview is active, look at drawview
3855 OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3856 if (pOlView && pOlView->GetWindow() == this)
3857 return pOlView->GetSurroundingText();
3860 return Window::GetSurroundingText();
3863 Selection ScGridWindow::GetSurroundingTextSelection() const
3865 bool bEditView = mrViewData.HasEditView(eWhich);
3866 if (bEditView)
3868 ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
3869 if (pHdl)
3870 return pHdl->GetSurroundingTextSelection();
3872 else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3874 // if no cell editview is active, look at drawview
3875 OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3876 if (pOlView && pOlView->GetWindow() == this)
3877 return pOlView->GetSurroundingTextSelection();
3880 return Window::GetSurroundingTextSelection();
3883 bool ScGridWindow::DeleteSurroundingText(const Selection& rSelection)
3885 bool bEditView = mrViewData.HasEditView(eWhich);
3886 if (bEditView)
3888 ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
3889 if (pHdl)
3890 return pHdl->DeleteSurroundingText(rSelection);
3892 else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3894 // if no cell editview is active, look at drawview
3895 OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3896 if (pOlView && pOlView->GetWindow() == this)
3897 return pOlView->DeleteSurroundingText(rSelection);
3900 return Window::DeleteSurroundingText(rSelection);
3903 void ScGridWindow::StopMarking()
3905 DrawEndAction(); // Cancel Select/move on Drawing-Layer
3907 if (nButtonDown)
3909 mrViewData.GetMarkData().SetMarking(false);
3910 nMouseStatus = SC_GM_IGNORE;
3914 void ScGridWindow::UpdateInputContext()
3916 bool bReadOnly = mrViewData.GetDocShell()->IsReadOnly();
3917 InputContextFlags nOptions = bReadOnly ? InputContextFlags::NONE : ( InputContextFlags::Text | InputContextFlags::ExtText );
3919 // when font from InputContext is used,
3920 // it must be taken from the cursor position's cell attributes
3922 InputContext aContext;
3923 aContext.SetOptions( nOptions );
3924 SetInputContext( aContext );
3927 // sensitive range (Pixel)
3928 #define SCROLL_SENSITIVE 20
3930 void ScGridWindow::DropScroll( const Point& rMousePos )
3932 ScDocument& rDoc = mrViewData.GetDocument();
3933 SCCOL nDx = 0;
3934 SCROW nDy = 0;
3935 Size aSize = GetOutputSizePixel();
3937 if (aSize.Width() > SCROLL_SENSITIVE * 3)
3939 if ( rMousePos.X() < SCROLL_SENSITIVE && mrViewData.GetPosX(WhichH(eWhich)) > 0 )
3940 nDx = -1;
3941 if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE
3942 && mrViewData.GetPosX(WhichH(eWhich)) < rDoc.MaxCol() )
3943 nDx = 1;
3945 if (aSize.Height() > SCROLL_SENSITIVE * 3)
3947 if ( rMousePos.Y() < SCROLL_SENSITIVE && mrViewData.GetPosY(WhichV(eWhich)) > 0 )
3948 nDy = -1;
3949 if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE
3950 && mrViewData.GetPosY(WhichV(eWhich)) < rDoc.MaxRow() )
3951 nDy = 1;
3954 if ( nDx != 0 || nDy != 0 )
3956 if ( nDx != 0 )
3957 mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
3958 if ( nDy != 0 )
3959 mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
3963 static bool lcl_TestScenarioRedliningDrop( const ScDocument* pDoc, const ScRange& aDragRange)
3965 // Test, if a scenario is affected by a drop when turing on RedLining,
3966 bool bReturn = false;
3967 SCTAB nTab = aDragRange.aStart.Tab();
3968 SCTAB nTabCount = pDoc->GetTableCount();
3970 if(pDoc->GetChangeTrack()!=nullptr)
3972 if( pDoc->IsScenario(nTab) && pDoc->HasScenarioRange(nTab, aDragRange))
3974 bReturn = true;
3976 else
3978 for(SCTAB i=nTab+1; i<nTabCount && pDoc->IsScenario(i); i++)
3980 if(pDoc->HasScenarioRange(i, aDragRange))
3982 bReturn = true;
3983 break;
3988 return bReturn;
3991 static ScRange lcl_MakeDropRange( const ScDocument& rDoc, SCCOL nPosX, SCROW nPosY, SCTAB nTab, const ScRange& rSource )
3993 SCCOL nCol1 = nPosX;
3994 SCCOL nCol2 = nCol1 + ( rSource.aEnd.Col() - rSource.aStart.Col() );
3995 if ( nCol2 > rDoc.MaxCol() )
3997 nCol1 -= nCol2 - rDoc.MaxCol();
3998 nCol2 = rDoc.MaxCol();
4000 SCROW nRow1 = nPosY;
4001 SCROW nRow2 = nRow1 + ( rSource.aEnd.Row() - rSource.aStart.Row() );
4002 if ( nRow2 > rDoc.MaxRow() )
4004 nRow1 -= nRow2 - rDoc.MaxRow();
4005 nRow2 = rDoc.MaxRow();
4008 return ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
4011 sal_Int8 ScGridWindow::AcceptPrivateDrop( const AcceptDropEvent& rEvt, const ScDragData& rData )
4013 if ( rEvt.mbLeaving )
4015 bDragRect = false;
4016 UpdateDragRectOverlay();
4017 return rEvt.mnAction;
4020 if ( rData.pCellTransfer )
4022 // Don't move source that would include filtered rows.
4023 if ((rEvt.mnAction & DND_ACTION_MOVE) && rData.pCellTransfer->HasFilteredRows())
4025 if (bDragRect)
4027 bDragRect = false;
4028 UpdateDragRectOverlay();
4030 return DND_ACTION_NONE;
4033 Point aPos = rEvt.maPosPixel;
4035 ScDocument* pSourceDoc = rData.pCellTransfer->GetSourceDocument();
4036 ScDocument& rThisDoc = mrViewData.GetDocument();
4037 if (pSourceDoc == &rThisDoc)
4039 OUString aName;
4040 if ( rThisDoc.HasChartAtPoint(mrViewData.GetTabNo(), PixelToLogic(aPos), aName ))
4042 if (bDragRect) // Remove rectangle
4044 bDragRect = false;
4045 UpdateDragRectOverlay();
4048 //! highlight chart? (selection border?)
4050 sal_Int8 nRet = rEvt.mnAction;
4051 return nRet;
4055 if (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) // whole sheet?
4057 bool bOk = rThisDoc.IsDocEditable();
4058 return bOk ? rEvt.mnAction : 0; // don't draw selection frame
4061 SCCOL nPosX;
4062 SCROW nPosY;
4063 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4065 ScRange aSourceRange = rData.pCellTransfer->GetRange();
4066 SCCOL nSourceStartX = aSourceRange.aStart.Col();
4067 SCROW nSourceStartY = aSourceRange.aStart.Row();
4068 SCCOL nSourceEndX = aSourceRange.aEnd.Col();
4069 SCROW nSourceEndY = aSourceRange.aEnd.Row();
4070 SCCOL nSizeX = nSourceEndX - nSourceStartX + 1;
4071 SCROW nSizeY = nSourceEndY - nSourceStartY + 1;
4073 if ( rEvt.mnAction != DND_ACTION_MOVE )
4074 nSizeY = rData.pCellTransfer->GetNonFilteredRows(); // copy/link: no filtered rows
4076 SCCOL nNewDragX = nPosX - rData.pCellTransfer->GetDragHandleX();
4077 if (nNewDragX<0) nNewDragX=0;
4078 if (nNewDragX+(nSizeX-1) > rThisDoc.MaxCol())
4079 nNewDragX = rThisDoc.MaxCol()-(nSizeX-1);
4080 SCROW nNewDragY = nPosY - rData.pCellTransfer->GetDragHandleY();
4081 if (nNewDragY<0) nNewDragY=0;
4082 if (nNewDragY+(nSizeY-1) > rThisDoc.MaxRow())
4083 nNewDragY = rThisDoc.MaxRow()-(nSizeY-1);
4085 // don't break scenario ranges, don't drop on filtered
4086 SCTAB nTab = mrViewData.GetTabNo();
4087 ScRange aDropRange = lcl_MakeDropRange( rThisDoc, nNewDragX, nNewDragY, nTab, aSourceRange );
4088 if ( lcl_TestScenarioRedliningDrop( &rThisDoc, aDropRange ) ||
4089 lcl_TestScenarioRedliningDrop( pSourceDoc, aSourceRange ) ||
4090 ScViewUtil::HasFiltered( aDropRange, rThisDoc) )
4092 if (bDragRect)
4094 bDragRect = false;
4095 UpdateDragRectOverlay();
4097 return DND_ACTION_NONE;
4100 InsCellCmd eDragInsertMode = INS_NONE;
4101 Window::PointerState aState = GetPointerState();
4103 // check for datapilot item sorting
4104 ScDPObject* pDPObj = nullptr;
4105 if ( &rThisDoc == pSourceDoc && ( pDPObj = rThisDoc.GetDPAtCursor( nNewDragX, nNewDragY, nTab ) ) != nullptr )
4107 // drop on DataPilot table: sort or nothing
4109 bool bDPSort = false;
4110 if ( rThisDoc.GetDPAtCursor( nSourceStartX, nSourceStartY, aSourceRange.aStart.Tab() ) == pDPObj )
4112 sheet::DataPilotTableHeaderData aDestData;
4113 pDPObj->GetHeaderPositionData( ScAddress(nNewDragX, nNewDragY, nTab), aDestData );
4114 bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
4116 // look through the source range
4117 for (SCROW nRow = aSourceRange.aStart.Row(); bValid && nRow <= aSourceRange.aEnd.Row(); ++nRow )
4118 for (SCCOL nCol = aSourceRange.aStart.Col(); bValid && nCol <= aSourceRange.aEnd.Col(); ++nCol )
4120 sheet::DataPilotTableHeaderData aSourceData;
4121 pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, aSourceRange.aStart.Tab() ), aSourceData );
4122 if ( aSourceData.Dimension != aDestData.Dimension || aSourceData.MemberName.isEmpty() )
4123 bValid = false; // empty (subtotal) or different field
4126 if ( bValid )
4128 bool bIsDataLayout;
4129 OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
4130 const ScDPSaveDimension* pDim = pDPObj->GetSaveData()->GetExistingDimensionByName( aDimName );
4131 if ( pDim )
4133 ScRange aOutRange = pDPObj->GetOutRange();
4135 sheet::DataPilotFieldOrientation nOrient = pDim->GetOrientation();
4136 if ( nOrient == sheet::DataPilotFieldOrientation_COLUMN )
4138 eDragInsertMode = INS_CELLSRIGHT;
4139 nSizeY = aOutRange.aEnd.Row() - nNewDragY + 1;
4140 bDPSort = true;
4142 else if ( nOrient == sheet::DataPilotFieldOrientation_ROW )
4144 eDragInsertMode = INS_CELLSDOWN;
4145 nSizeX = aOutRange.aEnd.Col() - nNewDragX + 1;
4146 bDPSort = true;
4152 if ( !bDPSort )
4154 // no valid sorting in a DataPilot table -> disallow
4155 if ( bDragRect )
4157 bDragRect = false;
4158 UpdateDragRectOverlay();
4160 return DND_ACTION_NONE;
4163 else if ( aState.mnState & KEY_MOD2 )
4165 if ( &rThisDoc == pSourceDoc && nTab == aSourceRange.aStart.Tab() )
4167 tools::Long nDeltaX = std::abs( static_cast< tools::Long >( nNewDragX - nSourceStartX ) );
4168 tools::Long nDeltaY = std::abs( static_cast< tools::Long >( nNewDragY - nSourceStartY ) );
4169 if ( nDeltaX <= nDeltaY )
4171 eDragInsertMode = INS_CELLSDOWN;
4173 else
4175 eDragInsertMode = INS_CELLSRIGHT;
4178 if ( ( eDragInsertMode == INS_CELLSDOWN && nNewDragY <= nSourceEndY &&
4179 ( nNewDragX + nSizeX - 1 ) >= nSourceStartX && nNewDragX <= nSourceEndX &&
4180 ( nNewDragX != nSourceStartX || nNewDragY >= nSourceStartY ) ) ||
4181 ( eDragInsertMode == INS_CELLSRIGHT && nNewDragX <= nSourceEndX &&
4182 ( nNewDragY + nSizeY - 1 ) >= nSourceStartY && nNewDragY <= nSourceEndY &&
4183 ( nNewDragY != nSourceStartY || nNewDragX >= nSourceStartX ) ) )
4185 if ( bDragRect )
4187 bDragRect = false;
4188 UpdateDragRectOverlay();
4190 return DND_ACTION_NONE;
4193 else
4195 if ( static_cast< tools::Long >( nSizeX ) >= static_cast< tools::Long >( nSizeY ) )
4197 eDragInsertMode = INS_CELLSDOWN;
4200 else
4202 eDragInsertMode = INS_CELLSRIGHT;
4207 if ( nNewDragX != nDragStartX || nNewDragY != nDragStartY ||
4208 nDragStartX+nSizeX-1 != nDragEndX || nDragStartY+nSizeY-1 != nDragEndY ||
4209 !bDragRect || eDragInsertMode != meDragInsertMode )
4211 nDragStartX = nNewDragX;
4212 nDragStartY = nNewDragY;
4213 nDragEndX = nDragStartX+nSizeX-1;
4214 nDragEndY = nDragStartY+nSizeY-1;
4215 bDragRect = true;
4216 meDragInsertMode = eDragInsertMode;
4218 UpdateDragRectOverlay();
4222 return rEvt.mnAction;
4225 sal_Int8 ScGridWindow::AcceptDrop( const AcceptDropEvent& rEvt )
4227 const ScDragData& rData = ScModule::get()->GetDragData();
4228 if ( rEvt.mbLeaving )
4230 DrawMarkDropObj( nullptr );
4231 return AcceptPrivateDrop( rEvt, rData ); // hide drop marker for internal D&D
4234 if ( mrViewData.GetDocShell()->IsReadOnly() )
4235 return DND_ACTION_NONE;
4237 ScDocument& rThisDoc = mrViewData.GetDocument();
4238 sal_Int8 nRet = DND_ACTION_NONE;
4240 if (rData.pCellTransfer)
4242 ScRange aSource = rData.pCellTransfer->GetRange();
4243 if ( aSource.aStart.Col() != 0 || aSource.aEnd.Col() != rThisDoc.MaxCol() ||
4244 aSource.aStart.Row() != 0 || aSource.aEnd.Row() != rThisDoc.MaxRow() )
4245 DropScroll( rEvt.maPosPixel );
4247 nRet = AcceptPrivateDrop( rEvt, rData );
4249 else
4251 if ( !rData.aLinkDoc.isEmpty() )
4253 OUString aThisName;
4254 ScDocShell* pDocSh = mrViewData.GetDocShell();
4255 if (pDocSh && pDocSh->HasName())
4256 aThisName = pDocSh->GetMedium()->GetName();
4258 if ( rData.aLinkDoc != aThisName )
4259 nRet = rEvt.mnAction;
4261 else if (!rData.aJumpTarget.isEmpty())
4263 // internal bookmarks (from Navigator)
4264 // local jumps from an unnamed document are possible only within a document
4266 if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
4267 nRet = rEvt.mnAction;
4269 else
4271 sal_Int8 nMyAction = rEvt.mnAction;
4273 // clear DND_ACTION_LINK when other actions are set. The usage below cannot handle
4274 // multiple set values
4275 if((nMyAction & DND_ACTION_LINK) && (nMyAction & DND_ACTION_COPYMOVE))
4277 nMyAction &= ~DND_ACTION_LINK;
4280 if ( !rData.pDrawTransfer ||
4281 !IsMyModel(rData.pDrawTransfer->GetDragSourceView()) ) // drawing within the document
4282 if ( rEvt.mbDefault && nMyAction == DND_ACTION_MOVE )
4283 nMyAction = DND_ACTION_COPY;
4285 SdrObject* pHitObj = rThisDoc.GetObjectAtPoint(
4286 mrViewData.GetTabNo(), PixelToLogic(rEvt.maPosPixel) );
4287 if ( pHitObj && nMyAction == DND_ACTION_LINK )
4289 if ( IsDropFormatSupported(SotClipboardFormatId::SVXB)
4290 || IsDropFormatSupported(SotClipboardFormatId::GDIMETAFILE)
4291 || IsDropFormatSupported(SotClipboardFormatId::PNG)
4292 || IsDropFormatSupported(SotClipboardFormatId::BITMAP) )
4294 // graphic dragged onto drawing object
4295 DrawMarkDropObj( pHitObj );
4296 nRet = nMyAction;
4299 if (!nRet)
4301 DrawMarkDropObj(nullptr);
4303 switch ( nMyAction )
4305 case DND_ACTION_COPY:
4306 case DND_ACTION_MOVE:
4307 case DND_ACTION_COPYMOVE:
4309 bool bMove = ( nMyAction == DND_ACTION_MOVE );
4310 if ( IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE ) ||
4311 IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
4312 IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
4313 IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
4314 IsDropFormatSupported( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
4315 IsDropFormatSupported( SotClipboardFormatId::STRING ) ||
4316 IsDropFormatSupported( SotClipboardFormatId::STRING_TSVC ) ||
4317 IsDropFormatSupported( SotClipboardFormatId::SYLK ) ||
4318 IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
4319 IsDropFormatSupported( SotClipboardFormatId::HTML ) ||
4320 IsDropFormatSupported( SotClipboardFormatId::HTML_SIMPLE ) ||
4321 IsDropFormatSupported( SotClipboardFormatId::DIF ) ||
4322 IsDropFormatSupported( SotClipboardFormatId::DRAWING ) ||
4323 IsDropFormatSupported( SotClipboardFormatId::SVXB ) ||
4324 IsDropFormatSupported( SotClipboardFormatId::RTF ) ||
4325 IsDropFormatSupported( SotClipboardFormatId::RICHTEXT ) ||
4326 IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ) ||
4327 IsDropFormatSupported( SotClipboardFormatId::PNG ) ||
4328 IsDropFormatSupported( SotClipboardFormatId::BITMAP ) ||
4329 IsDropFormatSupported( SotClipboardFormatId::SBA_DATAEXCHANGE ) ||
4330 IsDropFormatSupported( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) ||
4331 ( !bMove && (
4332 IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
4333 IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
4334 IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
4335 IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
4336 IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
4337 IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) ) ) )
4339 nRet = nMyAction;
4342 break;
4343 case DND_ACTION_LINK:
4344 if ( IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
4345 IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
4346 IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
4347 IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
4348 IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
4349 IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
4350 IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
4351 IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
4352 IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4354 nRet = nMyAction;
4356 break;
4359 if ( nRet )
4361 // Simple check for protection: It's not known here if the drop will result
4362 // in cells or drawing objects (some formats can be both) and how many cells
4363 // the result will be. But if IsFormatEditable for the drop cell position
4364 // is sal_False (ignores matrix formulas), nothing can be pasted, so the drop
4365 // can already be rejected here.
4367 Point aPos = rEvt.maPosPixel;
4368 SCCOL nPosX;
4369 SCROW nPosY;
4370 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4371 SCTAB nTab = mrViewData.GetTabNo();
4372 ScDocument& rDoc = mrViewData.GetDocument();
4374 ScEditableTester aTester( rDoc, nTab, nPosX,nPosY, nPosX,nPosY );
4375 if ( !aTester.IsFormatEditable() )
4376 nRet = DND_ACTION_NONE; // forbidden
4381 // scroll only for accepted formats
4382 if (nRet)
4383 DropScroll( rEvt.maPosPixel );
4386 return nRet;
4389 static SotClipboardFormatId lcl_GetDropFormatId( const uno::Reference<datatransfer::XTransferable>& xTransfer, bool bPreferText )
4391 TransferableDataHelper aDataHelper( xTransfer );
4393 if ( !aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
4395 // use bookmark formats if no sba is present
4397 if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
4398 return SotClipboardFormatId::SOLK;
4399 else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
4400 return SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
4401 else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
4402 return SotClipboardFormatId::NETSCAPE_BOOKMARK;
4403 else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4404 return SotClipboardFormatId::FILEGRPDESCRIPTOR;
4407 SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
4408 if ( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) )
4409 nFormatId = SotClipboardFormatId::DRAWING;
4410 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
4411 nFormatId = SotClipboardFormatId::SVXB;
4412 else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) )
4414 // If it's a Writer object, insert RTF instead of OLE
4416 bool bDoRtf = false;
4417 std::unique_ptr<SvStream> xStm;
4418 TransferableObjectDescriptor aObjDesc;
4419 if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) &&
4420 (xStm = aDataHelper.GetSotStorageStream( SotClipboardFormatId::EMBED_SOURCE )) )
4422 bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
4423 aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
4424 && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
4426 if ( bDoRtf )
4427 nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
4428 else
4429 nFormatId = SotClipboardFormatId::EMBED_SOURCE;
4431 else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
4432 nFormatId = SotClipboardFormatId::LINK_SOURCE;
4433 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
4434 nFormatId = SotClipboardFormatId::SBA_DATAEXCHANGE;
4435 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) )
4436 nFormatId = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
4437 else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_8 ) )
4438 nFormatId = SotClipboardFormatId::BIFF_8;
4439 else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_5 ) )
4440 nFormatId = SotClipboardFormatId::BIFF_5;
4441 else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) )
4442 nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
4443 else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) )
4444 nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
4445 else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
4446 nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
4447 else if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
4448 nFormatId = SotClipboardFormatId::RTF;
4449 else if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
4450 nFormatId = SotClipboardFormatId::RICHTEXT;
4451 else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML ) )
4452 nFormatId = SotClipboardFormatId::HTML;
4453 else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) )
4454 nFormatId = SotClipboardFormatId::HTML_SIMPLE;
4455 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SYLK ) )
4456 nFormatId = SotClipboardFormatId::SYLK;
4457 else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
4458 nFormatId = SotClipboardFormatId::LINK;
4459 else if ( bPreferText && aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) // #i86734# the behaviour introduced in #i62773# is wrong when pasting
4460 nFormatId = SotClipboardFormatId::STRING;
4461 else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
4462 nFormatId = SotClipboardFormatId::FILE_LIST;
4463 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) ) // #i62773# FILE_LIST/FILE before STRING (Unix file managers)
4464 nFormatId = SotClipboardFormatId::SIMPLE_FILE;
4465 else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING_TSVC ) )
4466 nFormatId = SotClipboardFormatId::STRING_TSVC;
4467 else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
4468 nFormatId = SotClipboardFormatId::STRING;
4469 else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
4470 nFormatId = SotClipboardFormatId::GDIMETAFILE;
4471 else if ( aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
4472 nFormatId = SotClipboardFormatId::PNG;
4473 else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) )
4474 nFormatId = SotClipboardFormatId::BITMAP;
4476 return nFormatId;
4479 static SotClipboardFormatId lcl_GetDropLinkId( const uno::Reference<datatransfer::XTransferable>& xTransfer )
4481 TransferableDataHelper aDataHelper( xTransfer );
4483 SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
4484 if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
4485 nFormatId = SotClipboardFormatId::LINK_SOURCE;
4486 else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
4487 nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
4488 else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
4489 nFormatId = SotClipboardFormatId::LINK;
4490 else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
4491 nFormatId = SotClipboardFormatId::FILE_LIST;
4492 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )
4493 nFormatId = SotClipboardFormatId::SIMPLE_FILE;
4494 else if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
4495 nFormatId = SotClipboardFormatId::SOLK;
4496 else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
4497 nFormatId = SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
4498 else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
4499 nFormatId = SotClipboardFormatId::NETSCAPE_BOOKMARK;
4500 else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4501 nFormatId = SotClipboardFormatId::FILEGRPDESCRIPTOR;
4503 return nFormatId;
4506 sal_Int8 ScGridWindow::ExecutePrivateDrop( const ExecuteDropEvent& rEvt, const ScDragData& rData )
4508 // hide drop marker
4509 bDragRect = false;
4510 UpdateDragRectOverlay();
4512 return DropTransferObj( rData.pCellTransfer, nDragStartX, nDragStartY,
4513 PixelToLogic(rEvt.maPosPixel), rEvt.mnAction );
4516 sal_Int8 ScGridWindow::DropTransferObj( ScTransferObj* pTransObj, SCCOL nDestPosX, SCROW nDestPosY,
4517 const Point& rLogicPos, sal_Int8 nDndAction )
4519 if ( !pTransObj )
4520 return 0;
4522 ScDocument* pSourceDoc = pTransObj->GetSourceDocument();
4523 ScDocShell* pDocSh = mrViewData.GetDocShell();
4524 ScDocument& rThisDoc = mrViewData.GetDocument();
4525 ScViewFunc* pView = mrViewData.GetView();
4526 SCTAB nThisTab = mrViewData.GetTabNo();
4527 ScDragSrc nFlags = pTransObj->GetDragSourceFlags();
4529 bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
4530 bool bIsMove = ( nDndAction == DND_ACTION_MOVE && !bIsNavi );
4532 // workaround for wrong nDndAction on Windows when pressing solely
4533 // the Alt key during drag and drop;
4534 // can be removed after #i79215# has been fixed
4535 if ( meDragInsertMode != INS_NONE )
4537 bIsMove = ( nDndAction & DND_ACTION_MOVE && !bIsNavi );
4540 bool bIsLink = ( nDndAction == DND_ACTION_LINK );
4542 ScRange aSource = pTransObj->GetRange();
4544 // only use visible tab from source range - when dragging within one table,
4545 // all selected tables at the time of dropping are used (handled in MoveBlockTo)
4546 SCTAB nSourceTab = pTransObj->GetVisibleTab();
4547 aSource.aStart.SetTab( nSourceTab );
4548 aSource.aEnd.SetTab( nSourceTab );
4550 SCCOL nSizeX = aSource.aEnd.Col() - aSource.aStart.Col() + 1;
4551 SCROW nSizeY = (bIsMove ? (aSource.aEnd.Row() - aSource.aStart.Row() + 1) :
4552 pTransObj->GetNonFilteredRows()); // copy/link: no filtered rows
4553 ScRange aDest( nDestPosX, nDestPosY, nThisTab,
4554 nDestPosX + nSizeX - 1, nDestPosY + nSizeY - 1, nThisTab );
4556 /* NOTE: AcceptPrivateDrop() already checked for filtered conditions during
4557 * dragging and adapted drawing of the selection frame. We check here
4558 * (again) because this may actually also be called from PasteSelection(),
4559 * we would have to duplicate determination of flags and destination range
4560 * and would lose the context of the "filtered destination is OK" cases
4561 * below, which is already awkward enough as is. */
4563 // Don't move filtered source.
4564 bool bFiltered = (bIsMove && pTransObj->HasFilteredRows());
4565 if (!bFiltered)
4567 if (pSourceDoc != &rThisDoc && ((nFlags & ScDragSrc::Table) ||
4568 (!bIsLink && meDragInsertMode == INS_NONE)))
4570 // Nothing. Either entire sheet to be dropped, or the one case
4571 // where PasteFromClip() is to be called that handles a filtered
4572 // destination itself. Drag-copy from another document without
4573 // inserting cells.
4575 else
4576 // Don't copy or move to filtered destination.
4577 bFiltered = ScViewUtil::HasFiltered(aDest, rThisDoc);
4580 bool bDone = false;
4582 if (!bFiltered && pSourceDoc == &rThisDoc)
4584 if (nFlags & ScDragSrc::Table) // whole sheet?
4586 if ( rThisDoc.IsDocEditable() )
4588 SCTAB nSrcTab = aSource.aStart.Tab();
4589 mrViewData.GetDocShell()->MoveTable( nSrcTab, nThisTab, !bIsMove, true ); // with Undo
4590 pView->SetTabNo( nThisTab, true );
4591 bDone = true;
4594 else // move/copy block
4596 OUString aChartName;
4597 if (rThisDoc.HasChartAtPoint( nThisTab, rLogicPos, aChartName ))
4599 OUString aRangeName(aSource.Format(rThisDoc, ScRefFlags::RANGE_ABS_3D,
4600 rThisDoc.GetAddressConvention()));
4601 SfxStringItem aNameItem( SID_CHART_NAME, aChartName );
4602 SfxStringItem aRangeItem( SID_CHART_SOURCE, aRangeName );
4603 sal_uInt16 nId = bIsMove ? SID_CHART_SOURCE : SID_CHART_ADDSOURCE;
4604 mrViewData.GetDispatcher().ExecuteList(nId,
4605 SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
4606 { &aRangeItem, &aNameItem });
4607 bDone = true;
4609 else if ( rThisDoc.GetDPAtCursor( nDestPosX, nDestPosY, nThisTab ) )
4611 // drop on DataPilot table: try to sort, fail if that isn't possible
4613 ScAddress aDestPos( nDestPosX, nDestPosY, nThisTab );
4614 if ( aDestPos != aSource.aStart )
4615 bDone = mrViewData.GetView()->DataPilotMove( aSource, aDestPos );
4616 else
4617 bDone = true; // same position: nothing
4619 else if ( nDestPosX != aSource.aStart.Col() || nDestPosY != aSource.aStart.Row() ||
4620 nSourceTab != nThisTab )
4622 OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
4623 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4625 SCCOL nCorrectCursorPosCol = 0;
4626 SCROW nCorrectCursorPosRow = 0;
4628 bDone = true;
4629 if ( meDragInsertMode != INS_NONE )
4631 // call with bApi = sal_True to avoid error messages in drop handler
4632 bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4633 if ( bDone )
4635 if ( nThisTab == nSourceTab )
4637 if ( meDragInsertMode == INS_CELLSDOWN &&
4638 nDestPosX == aSource.aStart.Col() && nDestPosY < aSource.aStart.Row() )
4640 ScRange aErrorRange( ScAddress::UNINITIALIZED );
4641 bDone = aSource.Move( 0, nSizeY, 0, aErrorRange, *pSourceDoc );
4642 nCorrectCursorPosRow = nSizeY;
4644 else if ( meDragInsertMode == INS_CELLSRIGHT &&
4645 nDestPosY == aSource.aStart.Row() && nDestPosX < aSource.aStart.Col() )
4647 ScRange aErrorRange( ScAddress::UNINITIALIZED );
4648 bDone = aSource.Move( nSizeX, 0, 0, aErrorRange, *pSourceDoc );
4649 nCorrectCursorPosCol = nSizeX;
4652 pDocSh->UpdateOle(mrViewData);
4653 pView->CellContentChanged();
4657 if ( bDone )
4659 if ( bIsLink )
4661 bDone = pView->LinkBlock( aSource, aDest.aStart );
4663 else
4665 bDone = pView->MoveBlockTo( aSource, aDest.aStart, bIsMove );
4669 if ( bDone && meDragInsertMode != INS_NONE && bIsMove && nThisTab == nSourceTab )
4671 DelCellCmd eCmd = DelCellCmd::NONE;
4672 if ( meDragInsertMode == INS_CELLSDOWN )
4674 eCmd = DelCellCmd::CellsUp;
4676 else if ( meDragInsertMode == INS_CELLSRIGHT )
4678 eCmd = DelCellCmd::CellsLeft;
4681 if ( ( eCmd == DelCellCmd::CellsUp && nDestPosX == aSource.aStart.Col() ) ||
4682 ( eCmd == DelCellCmd::CellsLeft && nDestPosY == aSource.aStart.Row() ) )
4684 // call with bApi = sal_True to avoid error messages in drop handler
4685 bDone = pDocSh->GetDocFunc().DeleteCells( aSource, nullptr, eCmd, true /*bApi*/ );
4686 if ( bDone )
4688 if ( eCmd == DelCellCmd::CellsUp && nDestPosY > aSource.aEnd.Row() )
4690 ScRange aErrorRange( ScAddress::UNINITIALIZED );
4691 bDone = aDest.Move( 0, -nSizeY, 0, aErrorRange, rThisDoc );
4693 else if ( eCmd == DelCellCmd::CellsLeft && nDestPosX > aSource.aEnd.Col() )
4695 ScRange aErrorRange( ScAddress::UNINITIALIZED );
4696 bDone = aDest.Move( -nSizeX, 0, 0, aErrorRange, rThisDoc );
4698 pDocSh->UpdateOle(mrViewData);
4699 pView->CellContentChanged();
4704 if ( bDone )
4706 pView->MarkRange( aDest, false );
4708 SCCOL nDCol;
4709 SCROW nDRow;
4710 if (pTransObj->WasSourceCursorInSelection())
4712 nDCol = pTransObj->GetSourceCursorX() - aSource.aStart.Col() + nCorrectCursorPosCol;
4713 nDRow = pTransObj->GetSourceCursorY() - aSource.aStart.Row() + nCorrectCursorPosRow;
4715 else
4717 nDCol = 0;
4718 nDRow = 0;
4720 pView->SetCursor( aDest.aStart.Col() + nDCol, aDest.aStart.Row() + nDRow );
4723 pDocSh->GetUndoManager()->LeaveListAction();
4726 else
4727 bDone = true; // nothing to do
4730 if (bDone)
4731 pTransObj->SetDragWasInternal(); // don't delete source in DragFinished
4733 else if ( !bFiltered && pSourceDoc ) // between documents
4735 if (nFlags & ScDragSrc::Table) // copy/link sheets between documents
4737 if ( rThisDoc.IsDocEditable() )
4739 ScDocShell* pSrcShell = pTransObj->GetSourceDocShell();
4741 std::vector<SCTAB> nTabs;
4743 ScMarkData aMark = pTransObj->GetSourceMarkData();
4744 SCTAB nTabCount = pSourceDoc->GetTableCount();
4746 for(SCTAB i=0; i<nTabCount; i++)
4748 if(aMark.GetTableSelect(i))
4750 nTabs.push_back(i);
4751 for(SCTAB j=i+1;j<nTabCount;j++)
4753 if((!pSourceDoc->IsVisible(j))&&(pSourceDoc->IsScenario(j)))
4755 nTabs.push_back( j );
4756 i=j;
4758 else break;
4763 pView->ImportTables( pSrcShell,static_cast<SCTAB>(nTabs.size()), nTabs.data(), bIsLink, nThisTab );
4764 bDone = true;
4767 else if ( bIsLink )
4769 // as in PasteDDE
4770 // (external references might be used instead?)
4772 ScDocShell* pSourceSh = pSourceDoc->GetDocumentShell();
4773 OSL_ENSURE(pSourceSh, "drag document has no shell");
4774 if (pSourceSh)
4776 OUString aUndo = ScResId( STR_UNDO_COPY );
4777 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4779 bDone = true;
4780 if ( meDragInsertMode != INS_NONE )
4782 // call with bApi = sal_True to avoid error messages in drop handler
4783 bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4784 if ( bDone )
4786 pDocSh->UpdateOle(mrViewData);
4787 pView->CellContentChanged();
4791 if ( bDone )
4793 OUString aApp = Application::GetAppName();
4794 OUString aTopic = pSourceSh->GetTitle( SFX_TITLE_FULLNAME );
4795 OUString aItem(aSource.Format(*pSourceDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
4797 // TODO: we could define ocQuote for "
4798 const OUString aQuote('"');
4799 const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
4800 OUString aFormula =
4801 "=" +
4802 ScCompiler::GetNativeSymbol(ocDde) +
4803 ScCompiler::GetNativeSymbol(ocOpen) +
4804 aQuote +
4805 aApp +
4806 aQuote +
4807 sSep +
4808 aQuote +
4809 aTopic +
4810 aQuote +
4811 sSep +
4812 aQuote +
4813 aItem +
4814 aQuote +
4815 ScCompiler::GetNativeSymbol(ocClose);
4817 pView->DoneBlockMode();
4818 pView->InitBlockMode( nDestPosX, nDestPosY, nThisTab );
4819 pView->MarkCursor( nDestPosX + nSizeX - 1,
4820 nDestPosY + nSizeY - 1, nThisTab );
4822 pView->EnterMatrix( aFormula, ::formula::FormulaGrammar::GRAM_NATIVE );
4824 pView->MarkRange( aDest, false );
4825 pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
4828 pDocSh->GetUndoManager()->LeaveListAction();
4831 else
4833 //! HasSelectedBlockMatrixFragment without selected sheet?
4834 //! or don't start dragging on a part of a matrix
4836 OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
4837 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4839 bDone = true;
4840 if ( meDragInsertMode != INS_NONE )
4842 // call with bApi = sal_True to avoid error messages in drop handler
4843 bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4844 if ( bDone )
4846 pDocSh->UpdateOle(mrViewData);
4847 pView->CellContentChanged();
4851 if ( bDone )
4853 pView->Unmark(); // before SetCursor, so CheckSelectionTransfer isn't called with a selection
4854 pView->SetCursor( nDestPosX, nDestPosY );
4855 bDone = pView->PasteFromClip( InsertDeleteFlags::ALL, pTransObj->GetDocument() ); // clip-doc
4856 if ( bDone )
4858 pView->MarkRange( aDest, false );
4859 pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
4863 pDocSh->GetUndoManager()->LeaveListAction();
4865 // no longer call ResetMark here - the inserted block has been selected
4866 // and may have been copied to primary selection
4870 sal_Int8 nRet = bDone ? nDndAction : DND_ACTION_NONE;
4871 return nRet;
4874 sal_Int8 ScGridWindow::ExecuteDrop( const ExecuteDropEvent& rEvt )
4876 DrawMarkDropObj( nullptr ); // drawing layer
4878 ScModule* pScMod = ScModule::get();
4879 const ScDragData& rData = pScMod->GetDragData();
4880 if (rData.pCellTransfer)
4881 return ExecutePrivateDrop( rEvt, rData );
4883 Point aPos = rEvt.maPosPixel;
4885 if ( !rData.aLinkDoc.isEmpty() )
4887 // try to insert a link
4889 bool bOk = true;
4890 OUString aThisName;
4891 ScDocShell* pDocSh = mrViewData.GetDocShell();
4892 if (pDocSh && pDocSh->HasName())
4893 aThisName = pDocSh->GetMedium()->GetName();
4895 if ( rData.aLinkDoc == aThisName ) // error - no link within a document
4896 bOk = false;
4897 else
4899 ScViewFunc* pView = mrViewData.GetView();
4900 if ( !rData.aLinkTable.isEmpty() )
4901 pView->InsertTableLink( rData.aLinkDoc, OUString(), OUString(),
4902 rData.aLinkTable );
4903 else if ( !rData.aLinkArea.isEmpty() )
4905 SCCOL nPosX;
4906 SCROW nPosY;
4907 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4908 pView->MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
4910 pView->InsertAreaLink( rData.aLinkDoc, OUString(), OUString(),
4911 rData.aLinkArea );
4913 else
4915 OSL_FAIL("drop with link: no sheet nor area");
4916 bOk = false;
4920 return bOk ? rEvt.mnAction : DND_ACTION_NONE; // don't try anything else
4923 Point aLogicPos = PixelToLogic(aPos);
4924 bool bIsLink = ( rEvt.mnAction == DND_ACTION_LINK );
4926 if (!bIsLink && rData.pDrawTransfer)
4928 ScDragSrc nFlags = rData.pDrawTransfer->GetDragSourceFlags();
4930 bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
4931 bool bIsMove = ( rEvt.mnAction == DND_ACTION_MOVE && !bIsNavi );
4933 bPasteIsMove = bIsMove;
4935 mrViewData.GetView()->PasteDraw(
4936 aLogicPos, rData.pDrawTransfer->GetModel(), false, u"A", u"B");
4938 if (bPasteIsMove)
4939 rData.pDrawTransfer->SetDragWasInternal();
4940 bPasteIsMove = false;
4942 return rEvt.mnAction;
4945 SCCOL nPosX;
4946 SCROW nPosY;
4947 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4949 if (!rData.aJumpTarget.isEmpty())
4951 // internal bookmark (from Navigator)
4952 // bookmark clipboard formats are in PasteScDataObject
4954 if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
4956 mrViewData.GetViewShell()->InsertBookmark( rData.aJumpText, rData.aJumpTarget,
4957 nPosX, nPosY );
4958 return rEvt.mnAction;
4962 ScDocument& rThisDoc = mrViewData.GetDocument();
4963 SdrObject* pHitObj = rThisDoc.GetObjectAtPoint( mrViewData.GetTabNo(), PixelToLogic(aPos) );
4964 if ( pHitObj && bIsLink )
4966 // dropped on drawing object
4967 // PasteOnDrawObjectLinked checks for valid formats
4968 if ( mrViewData.GetView()->PasteOnDrawObjectLinked( rEvt.maDropEvent.Transferable, *pHitObj ) )
4969 return rEvt.mnAction;
4972 bool bDone = false;
4974 SotClipboardFormatId nFormatId = bIsLink ?
4975 lcl_GetDropLinkId( rEvt.maDropEvent.Transferable ) :
4976 lcl_GetDropFormatId( rEvt.maDropEvent.Transferable, false );
4977 if ( nFormatId != SotClipboardFormatId::NONE )
4979 pScMod->SetInExecuteDrop( true ); // #i28468# prevent error messages from PasteDataFormat
4980 bDone = mrViewData.GetView()->PasteDataFormat(
4981 nFormatId, rEvt.maDropEvent.Transferable, nPosX, nPosY, &aLogicPos, bIsLink );
4982 pScMod->SetInExecuteDrop( false );
4985 sal_Int8 nRet = bDone ? rEvt.mnAction : DND_ACTION_NONE;
4986 return nRet;
4989 void ScGridWindow::PasteSelection( const Point& rPosPixel )
4991 Point aLogicPos = PixelToLogic( rPosPixel );
4993 SCCOL nPosX;
4994 SCROW nPosY;
4995 mrViewData.GetPosFromPixel( rPosPixel.X(), rPosPixel.Y(), eWhich, nPosX, nPosY );
4997 // If the mouse down was inside a visible note window, ignore it and
4998 // leave it up to the ScPostIt to handle it
4999 SdrView* pDrawView = mrViewData.GetViewShell()->GetScDrawView();
5000 if (pDrawView)
5002 const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList();
5003 const size_t nCount = rMarkList.GetMarkCount();
5004 for (size_t i = 0; i < nCount; ++i)
5006 SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
5007 if (pObj && pObj->GetLogicRect().Contains(aLogicPos))
5009 // Inside an active drawing object. Bail out.
5010 return;
5015 ScSelectionTransferObj* pOwnSelection = ScModule::get()->GetSelectionTransfer();
5016 if ( pOwnSelection )
5018 // within Calc
5020 // keep a reference to the data in case the selection is changed during paste
5021 rtl::Reference<ScTransferObj> pCellTransfer = pOwnSelection->GetCellData();
5022 if ( pCellTransfer )
5024 DropTransferObj( pCellTransfer.get(), nPosX, nPosY, aLogicPos, DND_ACTION_COPY );
5026 else
5028 // keep a reference to the data in case the selection is changed during paste
5029 rtl::Reference<ScDrawTransferObj> pDrawTransfer = pOwnSelection->GetDrawData();
5030 if ( pDrawTransfer )
5032 // bSameDocClipboard argument for PasteDraw is needed
5033 // because only DragData is checked directly inside PasteDraw
5034 mrViewData.GetView()->PasteDraw(
5035 aLogicPos, pDrawTransfer->GetModel(), false,
5036 pDrawTransfer->GetShellID(), SfxObjectShell::CreateShellID(mrViewData.GetDocShell()));
5040 else
5042 // get selection from system
5043 TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromPrimarySelection());
5044 const uno::Reference<datatransfer::XTransferable>& xTransferable = aDataHelper.GetTransferable();
5045 if ( xTransferable.is() )
5047 SotClipboardFormatId nFormatId = lcl_GetDropFormatId( xTransferable, true );
5048 if ( nFormatId != SotClipboardFormatId::NONE )
5049 mrViewData.GetView()->PasteDataFormat( nFormatId, xTransferable, nPosX, nPosY, &aLogicPos );
5054 void ScGridWindow::UpdateEditViewPos()
5056 if (!mrViewData.HasEditView(eWhich))
5057 return;
5059 EditView* pView;
5060 SCCOL nCol;
5061 SCROW nRow;
5062 mrViewData.GetEditView( eWhich, pView, nCol, nRow );
5063 SCCOL nEndCol = mrViewData.GetEditEndCol();
5064 SCROW nEndRow = mrViewData.GetEditEndRow();
5066 // hide EditView?
5068 bool bHide = ( nEndCol<mrViewData.GetPosX(eHWhich) || nEndRow<mrViewData.GetPosY(eVWhich) );
5069 if (ScModule::get()->IsFormulaMode())
5070 if ( mrViewData.GetTabNo() != mrViewData.GetRefTabNo() )
5071 bHide = true;
5073 if (bHide)
5075 tools::Rectangle aRect = pView->GetOutputArea();
5076 tools::Long nHeight = aRect.Bottom() - aRect.Top();
5077 aRect.SetTop( PixelToLogic(GetOutputSizePixel(), mrViewData.GetLogicMode()).
5078 Height() * 2 );
5079 aRect.SetBottom( aRect.Top() + nHeight );
5080 pView->SetOutputArea( aRect );
5081 pView->HideCursor();
5083 else
5085 // bForceToTop = sal_True for editing
5086 tools::Rectangle aPixRect = mrViewData.GetEditArea( eWhich, nCol, nRow, this, nullptr, true );
5088 if (comphelper::LibreOfficeKit::isActive() &&
5089 comphelper::LibreOfficeKit::isCompatFlagSet(
5090 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
5092 tools::Rectangle aPTwipsRect = mrViewData.GetEditArea(eWhich, nCol, nRow, this, nullptr,
5093 true, true /* bInPrintTwips */);
5094 tools::Rectangle aOutputAreaPTwips = pView->GetLOKSpecialOutputArea();
5095 aOutputAreaPTwips.SetPos(aPTwipsRect.TopLeft());
5096 pView->SetLOKSpecialOutputArea(aOutputAreaPTwips);
5099 Point aScrPos = PixelToLogic( aPixRect.TopLeft(), mrViewData.GetLogicMode() );
5101 tools::Rectangle aRect = pView->GetOutputArea();
5102 aRect.SetPos( aScrPos );
5103 pView->SetOutputArea( aRect );
5104 pView->ShowCursor();
5108 void ScGridWindow::ScrollPixel( tools::Long nDifX, tools::Long nDifY )
5110 ClickExtern();
5111 HideNoteMarker();
5113 SetMapMode(MapMode(MapUnit::MapPixel));
5114 Scroll( nDifX, nDifY, ScrollFlags::Children );
5115 SetMapMode( GetDrawMapMode() ); // generated shifted MapMode
5117 UpdateEditViewPos();
5119 DrawAfterScroll();
5122 // Update Formulas ------------------------------------------------------
5124 void ScGridWindow::UpdateFormulas(SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2)
5126 if (mrViewData.GetView()->IsMinimized())
5127 return;
5129 if ( nPaintCount )
5131 // Do not start, switched to paint
5132 // (then at least the MapMode would no longer be right)
5134 bNeedsRepaint = true; // -> at end of paint run Invalidate on all
5135 aRepaintPixel = tools::Rectangle(); // All
5136 return;
5139 if ( comphelper::LibreOfficeKit::isActive() )
5141 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5143 if (nX1 < 0)
5144 nX1 = pViewShell->GetLOKStartHeaderCol() + 1;
5145 if (nY1 < 0)
5146 nY1 = pViewShell->GetLOKStartHeaderRow() + 1;
5147 if (nX2 < 0)
5148 nX2 = pViewShell->GetLOKEndHeaderCol();
5149 if (nY2 < 0)
5150 nY2 = pViewShell->GetLOKEndHeaderRow();
5152 if (nX1 < 0 || nY1 < 0) return;
5154 // Consider frozen ranges not in main pane range as candidates
5155 // for update
5156 SCCOLROW nFreezeCol = mrViewData.GetLOKSheetFreezeIndex(true);
5157 SCCOLROW nFreezeRow = mrViewData.GetLOKSheetFreezeIndex(false);
5158 if ((nFreezeCol || nFreezeRow) && (nX1 || nY1))
5160 // top left
5161 if (nFreezeCol && nFreezeRow)
5162 UpdateFormulaRange(0, 0, nFreezeCol, nFreezeRow);
5163 // bottom left
5164 if (nFreezeCol && nX1)
5165 UpdateFormulaRange(0, nY1, nFreezeCol, nY2);
5166 // top right
5167 if (nFreezeRow && nY1)
5168 UpdateFormulaRange(nX1, 0, nX2, nFreezeRow);
5171 else
5173 nX1 = mrViewData.GetPosX( eHWhich );
5174 nY1 = mrViewData.GetPosY( eVWhich );
5175 nX2 = nX1 + mrViewData.VisibleCellsX( eHWhich );
5176 nY2 = nY1 + mrViewData.VisibleCellsY( eVWhich );
5179 UpdateFormulaRange(nX1, nY1, nX2, nY2);
5182 void ScGridWindow::UpdateFormulaRange(SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2)
5184 if (nX2 < nX1) nX2 = nX1;
5185 if (nY2 < nY1) nY2 = nY1;
5187 ScDocument& rDoc = mrViewData.GetDocument();
5189 if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
5190 if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();
5192 // Draw( nX1, nY1, nX2, nY2, SC_UPDATE_CHANGED );
5194 // don't draw directly - instead use OutputData to find changed area and invalidate
5196 SCROW nPosY = nY1;
5198 SCTAB nTab = mrViewData.GetTabNo();
5200 if ( !comphelper::LibreOfficeKit::isActive() )
5202 rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
5205 Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
5206 tools::Long nMirrorWidth = GetSizePixel().Width();
5207 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
5208 if ( bLayoutRTL )
5210 tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, nPosY, eWhich ).X();
5211 nMirrorWidth = aScrPos.X() - nEndPixel;
5212 aScrPos.setX( nEndPixel + 1 );
5215 tools::Long nScrX = aScrPos.X();
5216 tools::Long nScrY = aScrPos.Y();
5218 double nPPTX = mrViewData.GetPPTX();
5219 double nPPTY = mrViewData.GetPPTY();
5221 ScTableInfo aTabInfo(nY1, nY2, true);
5222 rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab, nPPTX, nPPTY, false, false );
5224 Fraction aZoomX = mrViewData.GetZoomX();
5225 Fraction aZoomY = mrViewData.GetZoomY();
5226 ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
5227 nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
5228 &aZoomX, &aZoomY );
5229 aOutputData.SetMirrorWidth( nMirrorWidth );
5231 aOutputData.FindChanged();
5233 // #i122149# do not use old GetChangedArea() which used polygon-based Regions, but use
5234 // the region-band based new version; anyways, only rectangles are added
5235 vcl::Region aChangedRegion( aOutputData.GetChangedAreaRegion() ); // logic (PixelToLogic)
5236 if(!aChangedRegion.IsEmpty())
5238 Invalidate(aChangedRegion);
5241 CheckNeedsRepaint(); // #i90362# used to be called via Draw() - still needed here
5244 void ScGridWindow::UpdateAutoFillMark(bool bMarked, const ScRange& rMarkRange)
5246 if ( bMarked != bAutoMarkVisible || ( bMarked && rMarkRange.aEnd != aAutoMarkPos ) )
5248 bAutoMarkVisible = bMarked;
5249 if ( bMarked )
5250 aAutoMarkPos = rMarkRange.aEnd;
5252 UpdateAutoFillOverlay();
5256 void ScGridWindow::updateLOKInputHelp(const OUString& title, const OUString& content) const
5258 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5260 boost::property_tree::ptree aTree;
5261 aTree.put("title", title);
5262 aTree.put("content", content);
5264 std::stringstream aStream;
5265 boost::property_tree::write_json(aStream, aTree);
5266 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_INPUT_HELP, OString(aStream.str()));
5269 void ScGridWindow::updateLOKValListButton( bool bVisible, const ScAddress& rPos ) const
5271 SCCOL nX = rPos.Col();
5272 SCROW nY = rPos.Row();
5273 std::stringstream ss;
5274 ss << nX << ", " << nY << ", " << static_cast<unsigned int>(bVisible);
5275 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5276 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_LIST_BUTTON, OString(ss.str()));
5279 void ScGridWindow::notifyKitCellFollowJump( ) const
5281 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5283 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SC_FOLLOW_JUMP, getCellCursor());
5286 void ScGridWindow::UpdateListValPos( bool bVisible, const ScAddress& rPos )
5288 bool bOldButton = bListValButton;
5289 ScAddress aOldPos = aListValPos;
5291 bListValButton = bVisible;
5292 aListValPos = rPos;
5294 if ( bListValButton )
5296 if ( !bOldButton || aListValPos != aOldPos )
5298 // paint area of new button
5299 if ( comphelper::LibreOfficeKit::isActive() )
5301 updateLOKValListButton( true, aListValPos );
5303 else
5305 Invalidate( PixelToLogic( GetListValButtonRect( aListValPos ) ) );
5309 if ( !bOldButton )
5310 return;
5312 if ( !bListValButton || aListValPos != aOldPos )
5314 // paint area of old button
5315 if ( comphelper::LibreOfficeKit::isActive() )
5317 updateLOKValListButton( false, aOldPos );
5319 else
5321 Invalidate( PixelToLogic( GetListValButtonRect( aOldPos ) ) );
5326 void ScGridWindow::HideCursor()
5328 ++nCursorHideCount;
5331 void ScGridWindow::ShowCursor()
5333 --nCursorHideCount;
5336 void ScGridWindow::GetFocus()
5338 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5339 pViewShell->SetFormShellAtTop( false ); // focus in GridWindow -> FormShell no longer on top
5341 if (pViewShell->HasAccessibilityObjects())
5342 pViewShell->BroadcastAccessibility(ScAccGridWinFocusGotHint(eWhich));
5344 if (!ScModule::get()->IsFormulaMode())
5346 pViewShell->UpdateInputHandler();
5347 // StopMarking(); // If Dialog (error), because then no ButtonUp
5348 // MO: only when not in RefInput mode
5349 // -> GetFocus/MouseButtonDown order on Mac
5352 mrViewData.GetDocShell()->CheckConfigOptions();
5353 Window::GetFocus();
5356 void ScGridWindow::LoseFocus()
5358 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
5360 if (pViewShell && pViewShell->HasAccessibilityObjects())
5361 pViewShell->BroadcastAccessibility(ScAccGridWinFocusLostHint(eWhich));
5363 Window::LoseFocus();
5366 bool ScGridWindow::HitRangeFinder( const Point& rMouse, RfCorner& rCorner,
5367 sal_uInt16* pIndex, SCCOL* pAddX, SCROW* pAddY)
5369 bool bFound = false;
5370 ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
5371 if (pHdl)
5373 ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
5374 if ( pRangeFinder && !pRangeFinder->IsHidden() &&
5375 pRangeFinder->GetDocName() == mrViewData.GetDocShell()->GetTitle() )
5377 ScDocument& rDoc = mrViewData.GetDocument();
5378 SCTAB nTab = mrViewData.GetTabNo();
5379 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
5380 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
5382 SCCOL nPosX;
5383 SCROW nPosY;
5384 mrViewData.GetPosFromPixel( rMouse.X(), rMouse.Y(), eWhich, nPosX, nPosY );
5385 // merged (single/Range) ???
5386 ScAddress aAddr( nPosX, nPosY, nTab );
5388 Point aCellStart = mrViewData.GetScrPos( nPosX, nPosY, eWhich, true );
5389 Point aCellEnd = aCellStart;
5390 tools::Long nSizeXPix;
5391 tools::Long nSizeYPix;
5392 mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeXPix, nSizeYPix );
5394 aCellEnd.AdjustX(nSizeXPix * nLayoutSign );
5395 aCellEnd.AdjustY(nSizeYPix );
5397 bool bCornerHorizontalRight;
5398 bool bCornerHorizontalLeft;
5399 if ( bLayoutRTL )
5401 bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() && rMouse.X() <= aCellEnd.X() + 8 );
5402 bCornerHorizontalLeft = ( rMouse.X() >= aCellStart.X() - 8 && rMouse.X() <= aCellStart.X() );
5404 else
5406 bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() - 8 && rMouse.X() <= aCellEnd.X() );
5407 bCornerHorizontalLeft = ( rMouse.X() >= aCellStart.X() && rMouse.X() <= aCellStart.X() + 8 );
5410 bool bCornerVerticalDown = rMouse.Y() >= aCellEnd.Y() - 8 && rMouse.Y() <= aCellEnd.Y();
5411 bool bCornerVerticalUp = rMouse.Y() >= aCellStart.Y() && rMouse.Y() <= aCellStart.Y() + 8;
5413 // corner is hit only if the mouse is within the cell
5414 sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
5415 for (sal_uInt16 i=nCount; i;)
5417 // search backwards so that the last repainted frame is found
5418 --i;
5419 ScRangeFindData& rData = pRangeFinder->GetObject(i);
5420 if ( rData.aRef.Contains(aAddr) )
5422 if (pIndex)
5423 *pIndex = i;
5424 if (pAddX)
5425 *pAddX = nPosX - rData.aRef.aStart.Col();
5426 if (pAddY)
5427 *pAddY = nPosY - rData.aRef.aStart.Row();
5429 bFound = true;
5431 rCorner = NONE;
5433 ScAddress aEnd = rData.aRef.aEnd;
5434 ScAddress aStart = rData.aRef.aStart;
5436 if ( bCornerHorizontalLeft && bCornerVerticalUp &&
5437 aAddr == aStart)
5439 rCorner = LEFT_UP;
5441 else if (bCornerHorizontalRight && bCornerVerticalDown &&
5442 aAddr == aEnd)
5444 rCorner = RIGHT_DOWN;
5446 else if (bCornerHorizontalRight && bCornerVerticalUp &&
5447 aAddr == ScAddress(aEnd.Col(), aStart.Row(), aStart.Tab()))
5449 rCorner = RIGHT_UP;
5451 else if (bCornerHorizontalLeft && bCornerVerticalDown &&
5452 aAddr == ScAddress(aStart.Col(), aEnd.Row(), aStart.Tab()))
5454 rCorner = LEFT_DOWN;
5456 break;
5461 return bFound;
5464 #define SCE_TOP 1
5465 #define SCE_BOTTOM 2
5466 #define SCE_LEFT 4
5467 #define SCE_RIGHT 8
5468 #define SCE_ALL 15
5470 static void lcl_PaintOneRange( ScDocShell* pDocSh, const ScRange& rRange, sal_uInt16 nEdges )
5472 // the range is always properly oriented
5474 SCCOL nCol1 = rRange.aStart.Col();
5475 SCROW nRow1 = rRange.aStart.Row();
5476 SCTAB nTab1 = rRange.aStart.Tab();
5477 SCCOL nCol2 = rRange.aEnd.Col();
5478 SCROW nRow2 = rRange.aEnd.Row();
5479 SCTAB nTab2 = rRange.aEnd.Tab();
5480 bool bHiddenEdge = false;
5481 SCROW nTmp;
5483 ScDocument& rDoc = pDocSh->GetDocument();
5484 while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab1) )
5486 --nCol1;
5487 bHiddenEdge = true;
5489 while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab1) )
5491 ++nCol2;
5492 bHiddenEdge = true;
5494 nTmp = rDoc.FirstVisibleRow(0, nRow1, nTab1);
5495 if (!rDoc.ValidRow(nTmp))
5496 nTmp = 0;
5497 if (nTmp < nRow1)
5499 nRow1 = nTmp;
5500 bHiddenEdge = true;
5502 nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab1);
5503 if (!rDoc.ValidRow(nTmp))
5504 nTmp = rDoc.MaxRow();
5505 if (nTmp > nRow2)
5507 nRow2 = nTmp;
5508 bHiddenEdge = true;
5511 if ( nCol2 > nCol1 + 1 && nRow2 > nRow1 + 1 && !bHiddenEdge )
5513 // Only along the edges (The corners are hit twice)
5514 if ( nEdges & SCE_TOP )
5515 pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow1, nTab2, PaintPartFlags::Marks );
5516 if ( nEdges & SCE_LEFT )
5517 pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol1, nRow2, nTab2, PaintPartFlags::Marks );
5518 if ( nEdges & SCE_RIGHT )
5519 pDocSh->PostPaint( nCol2, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
5520 if ( nEdges & SCE_BOTTOM )
5521 pDocSh->PostPaint( nCol1, nRow2, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
5523 else // everything in one call
5524 pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
5527 static void lcl_PaintRefChanged( ScDocShell* pDocSh, const ScRange& rOldUn, const ScRange& rNewUn )
5529 // Repaint for the parts of the frame in old, which in are no more in New
5531 ScRange aOld = rOldUn;
5532 ScRange aNew = rNewUn;
5533 aOld.PutInOrder();
5534 aNew.PutInOrder();
5536 if ( aOld.aStart == aOld.aEnd ) //! Ignore sheet ?
5537 pDocSh->GetDocument().ExtendMerge(aOld);
5538 if ( aNew.aStart == aNew.aEnd ) //! Ignore sheet ?
5539 pDocSh->GetDocument().ExtendMerge(aNew);
5541 SCCOL nOldCol1 = aOld.aStart.Col();
5542 SCROW nOldRow1 = aOld.aStart.Row();
5543 SCCOL nOldCol2 = aOld.aEnd.Col();
5544 SCROW nOldRow2 = aOld.aEnd.Row();
5545 SCCOL nNewCol1 = aNew.aStart.Col();
5546 SCROW nNewRow1 = aNew.aStart.Row();
5547 SCCOL nNewCol2 = aNew.aEnd.Col();
5548 SCROW nNewRow2 = aNew.aEnd.Row();
5549 SCTAB nTab1 = aOld.aStart.Tab(); // sheet is not changed
5550 SCTAB nTab2 = aOld.aEnd.Tab();
5552 if ( nNewRow2 < nOldRow1 || nNewRow1 > nOldRow2 ||
5553 nNewCol2 < nOldCol1 || nNewCol1 > nOldCol2 ||
5554 ( nNewCol1 != nOldCol1 && nNewRow1 != nOldRow1 &&
5555 nNewCol2 != nOldCol2 && nNewRow2 != nOldRow2 ) )
5557 // Completely removed or changed all sides
5558 // (check <= instead of < goes wrong for single rows/columns)
5560 lcl_PaintOneRange( pDocSh, aOld, SCE_ALL );
5562 else // Test all four corners separately
5564 // upper part
5565 if ( nNewRow1 < nOldRow1 ) // only delete upper line
5566 lcl_PaintOneRange( pDocSh, ScRange(
5567 nOldCol1, nOldRow1, nTab1, nOldCol2, nOldRow1, nTab2 ), SCE_ALL );
5568 else if ( nNewRow1 > nOldRow1 ) // the upper part which is will be removed
5569 lcl_PaintOneRange( pDocSh, ScRange(
5570 nOldCol1, nOldRow1, nTab1, nOldCol2, nNewRow1-1, nTab2 ),
5571 SCE_ALL &~ SCE_BOTTOM );
5573 // bottom part
5574 if ( nNewRow2 > nOldRow2 ) // only delete bottom line
5575 lcl_PaintOneRange( pDocSh, ScRange(
5576 nOldCol1, nOldRow2, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
5577 else if ( nNewRow2 < nOldRow2 ) // the bottom part which is will be removed
5578 lcl_PaintOneRange( pDocSh, ScRange(
5579 nOldCol1, nNewRow2+1, nTab1, nOldCol2, nOldRow2, nTab2 ),
5580 SCE_ALL &~ SCE_TOP );
5582 // left part
5583 if ( nNewCol1 < nOldCol1 ) // only delete left line
5584 lcl_PaintOneRange( pDocSh, ScRange(
5585 nOldCol1, nOldRow1, nTab1, nOldCol1, nOldRow2, nTab2 ), SCE_ALL );
5586 else if ( nNewCol1 > nOldCol1 ) // the left part which is will be removed
5587 lcl_PaintOneRange( pDocSh, ScRange(
5588 nOldCol1, nOldRow1, nTab1, nNewCol1-1, nOldRow2, nTab2 ),
5589 SCE_ALL &~ SCE_RIGHT );
5591 // right part
5592 if ( nNewCol2 > nOldCol2 ) // only delete right line
5593 lcl_PaintOneRange( pDocSh, ScRange(
5594 nOldCol2, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
5595 else if ( nNewCol2 < nOldCol2 ) // the right part which is will be removed
5596 lcl_PaintOneRange( pDocSh, ScRange(
5597 nNewCol2+1, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ),
5598 SCE_ALL &~ SCE_LEFT );
5602 void ScGridWindow::RFMouseMove( const MouseEvent& rMEvt, bool bUp )
5604 ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
5605 if (!pHdl)
5606 return;
5607 ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
5608 if (!pRangeFinder || nRFIndex >= pRangeFinder->Count())
5609 return;
5610 ScRangeFindData& rData = pRangeFinder->GetObject( nRFIndex );
5612 // Mouse pointer
5614 if (bRFSize)
5615 SetPointer( PointerStyle::Cross );
5616 else
5617 SetPointer( PointerStyle::Hand );
5619 // Scrolling
5621 bool bTimer = false;
5622 Point aPos = rMEvt.GetPosPixel();
5623 SCCOL nDx = 0;
5624 SCROW nDy = 0;
5625 if ( aPos.X() < 0 ) nDx = -1;
5626 if ( aPos.Y() < 0 ) nDy = -1;
5627 Size aSize = GetOutputSizePixel();
5628 if ( aPos.X() >= aSize.Width() )
5629 nDx = 1;
5630 if ( aPos.Y() >= aSize.Height() )
5631 nDy = 1;
5632 if ( nDx != 0 || nDy != 0 )
5634 if ( nDx != 0) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
5635 if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
5636 bTimer = true;
5639 // Switching when fixating (so Scrolling works)
5641 if ( eWhich == mrViewData.GetActivePart() ) //??
5643 if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX )
5644 if ( nDx > 0 )
5646 if ( eWhich == SC_SPLIT_TOPLEFT )
5647 mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
5648 else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
5649 mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
5652 if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX )
5653 if ( nDy > 0 )
5655 if ( eWhich == SC_SPLIT_TOPLEFT )
5656 mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
5657 else if ( eWhich == SC_SPLIT_TOPRIGHT )
5658 mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
5662 // Move
5664 SCCOL nPosX;
5665 SCROW nPosY;
5666 mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
5668 ScRange aOld = rData.aRef;
5669 ScRange aNew = aOld;
5670 if ( bRFSize )
5672 switch (aRFSelectedCorned)
5674 case LEFT_UP:
5675 aNew.aStart.SetCol(nPosX);
5676 aNew.aStart.SetRow(nPosY);
5677 break;
5678 case LEFT_DOWN:
5679 aNew.aStart.SetCol(nPosX);
5680 aNew.aEnd.SetRow(nPosY);
5681 break;
5682 case RIGHT_UP:
5683 aNew.aEnd.SetCol(nPosX);
5684 aNew.aStart.SetRow(nPosY);
5685 break;
5686 case RIGHT_DOWN:
5687 aNew.aEnd.SetCol(nPosX);
5688 aNew.aEnd.SetRow(nPosY);
5689 break;
5690 default:
5691 break;
5694 else
5696 ScDocument& rDoc = mrViewData.GetDocument();
5697 tools::Long nStartX = nPosX - nRFAddX;
5698 if ( nStartX < 0 ) nStartX = 0;
5699 tools::Long nStartY = nPosY - nRFAddY;
5700 if ( nStartY < 0 ) nStartY = 0;
5701 tools::Long nEndX = nStartX + aOld.aEnd.Col() - aOld.aStart.Col();
5702 if ( nEndX > rDoc.MaxCol() )
5704 nStartX -= ( nEndX - rDoc.MaxRow() );
5705 nEndX = rDoc.MaxCol();
5707 tools::Long nEndY = nStartY + aOld.aEnd.Row() - aOld.aStart.Row();
5708 if ( nEndY > rDoc.MaxRow() )
5710 nStartY -= ( nEndY - rDoc.MaxRow() );
5711 nEndY = rDoc.MaxRow();
5714 aNew.aStart.SetCol(static_cast<SCCOL>(nStartX));
5715 aNew.aStart.SetRow(static_cast<SCROW>(nStartY));
5716 aNew.aEnd.SetCol(static_cast<SCCOL>(nEndX));
5717 aNew.aEnd.SetRow(static_cast<SCROW>(nEndY));
5720 if ( bUp )
5721 aNew.PutInOrder(); // For ButtonUp again in the proper order
5723 if ( aNew != aOld )
5725 pHdl->UpdateRange( nRFIndex, aNew );
5727 ScDocShell* pDocSh = mrViewData.GetDocShell();
5729 pHdl->UpdateLokReferenceMarks();
5731 // only redrawing what has been changed...
5732 lcl_PaintRefChanged( pDocSh, aOld, aNew );
5734 // only redraw new frame (synchronously)
5735 pDocSh->Broadcast( ScIndexHint( SfxHintId::ScShowRangeFinder, nRFIndex ) );
5737 PaintImmediately(); // what you move, will be seen immediately
5740 // Timer for Scrolling
5742 if (bTimer)
5743 mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
5744 else
5745 mrViewData.GetView()->ResetTimer();
5748 namespace {
5750 SvxAdjust toSvxAdjust( const ScPatternAttr& rPat )
5752 SvxCellHorJustify eHorJust =
5753 rPat.GetItem(ATTR_HOR_JUSTIFY).GetValue();
5755 SvxAdjust eSvxAdjust = SvxAdjust::Left;
5756 switch (eHorJust)
5758 case SvxCellHorJustify::Left:
5759 case SvxCellHorJustify::Repeat: // not implemented
5760 case SvxCellHorJustify::Standard: // always Text if an EditCell type
5761 eSvxAdjust = SvxAdjust::Left;
5762 break;
5763 case SvxCellHorJustify::Right:
5764 eSvxAdjust = SvxAdjust::Right;
5765 break;
5766 case SvxCellHorJustify::Center:
5767 eSvxAdjust = SvxAdjust::Center;
5768 break;
5769 case SvxCellHorJustify::Block:
5770 eSvxAdjust = SvxAdjust::Block;
5771 break;
5774 return eSvxAdjust;
5777 std::shared_ptr<ScFieldEditEngine> createEditEngine( ScDocShell* pDocSh, const ScPatternAttr& rPat )
5779 ScDocument& rDoc = pDocSh->GetDocument();
5781 auto pEngine = std::make_shared<ScFieldEditEngine>(&rDoc, rDoc.GetEditPool());
5782 ScSizeDeviceProvider aProv(pDocSh);
5783 pEngine->SetRefDevice(aProv.GetDevice());
5784 pEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
5785 auto pDefault = std::make_unique<SfxItemSet>(pEngine->GetEmptyItemSet());
5786 rPat.FillEditItemSet(pDefault.get());
5787 pDefault->Put(SvxAdjustItem(toSvxAdjust(rPat), EE_PARA_JUST));
5788 pEngine->SetDefaults(std::move(pDefault));
5790 return pEngine;
5793 bool extractURLInfo( const SvxFieldItem* pFieldItem, OUString* pName, OUString* pUrl, OUString* pTarget )
5795 if (!pFieldItem)
5796 return false;
5798 const SvxFieldData* pField = pFieldItem->GetField();
5799 if (pField->GetClassId() != text::textfield::Type::URL)
5800 return false;
5802 const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
5804 if (pName)
5805 *pName = pURLField->GetRepresentation();
5806 if (pUrl)
5807 *pUrl = pURLField->GetURL();
5808 if (pTarget)
5809 *pTarget = pURLField->GetTargetFrame();
5811 return true;
5816 static void lcl_SetEngineTextKeepingDefaults(const std::shared_ptr<ScFieldEditEngine>& pEngine,
5817 ScDocument& rDoc, ScRefCellValue& rCell, const OUString& rURL)
5819 std::unique_ptr<EditTextObject> pTextObj;
5820 if (rCell.getType() == CELLTYPE_EDIT)
5822 if (rCell.getEditText())
5823 pEngine->SetTextCurrentDefaults(*rCell.getEditText());
5825 else // Not an Edit cell and is a formula cell with 'Hyperlink'
5826 // function if we have no URL, otherwise it could be a formula
5827 // cell ( or other type ? ) with a hyperlink associated with it.
5829 if (rURL.isEmpty())
5830 pTextObj = rCell.getFormula()->CreateURLObject();
5831 else
5833 OUString aRepres = rURL;
5835 // TODO: text content of formatted numbers can be different
5836 if (rCell.hasNumeric())
5837 aRepres = OUString::number(rCell.getValue());
5838 else if (rCell.getType() == CELLTYPE_FORMULA)
5839 aRepres = rCell.getFormula()->GetString().getString();
5841 pTextObj = ScEditUtil::CreateURLObjectFromURL(rDoc, rURL, aRepres);
5844 if (pTextObj)
5845 pEngine->SetTextCurrentDefaults(*pTextObj);
5849 static std::vector<std::unique_ptr<SvxFieldItem>> lcl_GetEditEngineFields(const ScFieldEditEngine& rEditEngine)
5851 std::vector<std::unique_ptr<SvxFieldItem>> vFieldVect;
5853 sal_Int32 nPara = rEditEngine.GetParagraphCount();
5854 for (sal_Int32 nCurrPara = 0; nCurrPara < nPara; ++nCurrPara)
5856 for (EFieldInfo& rFieldInfo : rEditEngine.GetFieldInfo(nCurrPara))
5858 vFieldVect.push_back(std::move(rFieldInfo.pFieldItem));
5861 return vFieldVect;
5865 std::vector<UrlData> ScGridWindow::GetEditUrls(const ScAddress& rSelectedCell)
5867 ScDocShell* pDocSh = mrViewData.GetDocShell();
5868 ScDocument& rDoc = pDocSh->GetDocument();
5870 SCCOL nPosX = rSelectedCell.Col();
5871 SCROW nPosY = rSelectedCell.Row();
5872 SCTAB nTab = rSelectedCell.Tab();
5874 OUString sURL;
5875 ScRefCellValue aCell;
5876 std::vector<UrlData> vUrls;
5877 if (!lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
5878 return vUrls;
5880 if (nPosX != rSelectedCell.Col())
5881 return vUrls;
5883 const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
5885 std::shared_ptr<ScFieldEditEngine> pEngine = createEditEngine(pDocSh, *pPattern);
5887 lcl_SetEngineTextKeepingDefaults(pEngine, rDoc, aCell, sURL);
5889 std::vector<std::unique_ptr<SvxFieldItem>> vFieldItems = lcl_GetEditEngineFields(*pEngine);
5890 for (auto& pFieldItem : vFieldItems)
5892 UrlData aData;
5893 bool bIsUrl = extractURLInfo(pFieldItem.get(), &aData.aName, &aData.aUrl, &aData.aTarget);
5894 if (bIsUrl && !aData.aUrl.isEmpty())
5895 vUrls.push_back(aData);
5897 return vUrls;
5900 bool ScGridWindow::GetEditUrl(const Point& rPos, OUString* pName, OUString* pUrl, OUString* pTarget,
5901 SCCOL* pnCol)
5903 ScTabViewShell* pViewSh = mrViewData.GetViewShell();
5904 ScInputHandler* pInputHdl = nullptr;
5905 if (pViewSh)
5906 pInputHdl = pViewSh->GetInputHandler();
5907 EditView* pView = (pInputHdl && pInputHdl->IsInputMode()) ? pInputHdl->GetTableView() : nullptr;
5908 if (pView)
5909 return extractURLInfo(pView->GetFieldUnderMousePointer(), pName, pUrl, pTarget);
5911 //! Pass on nPosX/Y?
5912 SCCOL nPosX;
5913 SCROW nPosY;
5914 mrViewData.GetPosFromPixel( rPos.X(), rPos.Y(), eWhich, nPosX, nPosY );
5916 SCTAB nTab = mrViewData.GetTabNo();
5917 ScDocShell* pDocSh = mrViewData.GetDocShell();
5918 ScDocument& rDoc = pDocSh->GetDocument();
5919 OUString sURL;
5920 ScRefCellValue aCell;
5921 bool bFound = lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL);
5922 if( !bFound )
5923 return false;
5924 if (pnCol)
5925 *pnCol = nPosX;
5927 const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
5928 // bForceToTop = sal_False, use the cell's real position
5929 tools::Rectangle aEditRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, false );
5930 if (rPos.Y() < aEditRect.Top())
5931 return false;
5933 // vertical can not (yet) be clicked:
5935 if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
5936 return false;
5938 bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
5939 (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block);
5940 SvxCellHorJustify eHorJust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
5942 // EditEngine
5944 std::shared_ptr<ScFieldEditEngine> pEngine = createEditEngine(pDocSh, *pPattern);
5946 MapMode aEditMode = mrViewData.GetLogicMode(eWhich); // without draw scaling
5947 tools::Rectangle aLogicEdit = PixelToLogic( aEditRect, aEditMode );
5948 tools::Long nThisColLogic = aLogicEdit.Right() - aLogicEdit.Left() + 1;
5949 Size aPaperSize( 1000000, 1000000 );
5950 if (aCell.getType() == CELLTYPE_FORMULA)
5952 tools::Long nSizeX = 0;
5953 tools::Long nSizeY = 0;
5954 mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeX, nSizeY );
5955 aPaperSize = Size(nSizeX, nSizeY );
5956 aPaperSize = PixelToLogic(aPaperSize);
5959 if (bBreak)
5960 aPaperSize.setWidth( nThisColLogic );
5961 pEngine->SetPaperSize( aPaperSize );
5963 lcl_SetEngineTextKeepingDefaults(pEngine, rDoc, aCell, sURL);
5965 tools::Long nStartX = aLogicEdit.Left();
5967 tools::Long nTextWidth = pEngine->CalcTextWidth();
5968 tools::Long nTextHeight = pEngine->GetTextHeight();
5969 if ( nTextWidth < nThisColLogic )
5971 if (eHorJust == SvxCellHorJustify::Right)
5972 nStartX += nThisColLogic - nTextWidth;
5973 else if (eHorJust == SvxCellHorJustify::Center)
5974 nStartX += (nThisColLogic - nTextWidth) / 2;
5977 aLogicEdit.SetLeft( nStartX );
5978 if (!bBreak)
5979 aLogicEdit.SetRight( nStartX + nTextWidth );
5981 // There is one glitch when dealing with a hyperlink cell and
5982 // the cell content is NUMERIC. This defaults to right aligned and
5983 // we need to adjust accordingly.
5984 if (aCell.hasNumeric() && eHorJust == SvxCellHorJustify::Standard)
5986 aLogicEdit.SetRight( aLogicEdit.Left() + nThisColLogic - 1 );
5987 aLogicEdit.SetLeft( aLogicEdit.Right() - nTextWidth );
5989 aLogicEdit.SetBottom( aLogicEdit.Top() + nTextHeight );
5991 Point aLogicClick = PixelToLogic(rPos,aEditMode);
5992 if ( aLogicEdit.Contains(aLogicClick) )
5994 EditView aTempView(pEngine.get(), this);
5995 aTempView.SetOutputArea( aLogicEdit );
5997 bool bRet;
5998 if (comphelper::LibreOfficeKit::isActive())
6000 bRet = extractURLInfo(aTempView.GetField(aLogicClick), pName, pUrl, pTarget);
6002 else
6004 MapMode aOld = GetMapMode();
6005 SetMapMode(aEditMode); // no return anymore
6006 bRet = extractURLInfo(aTempView.GetFieldUnderMousePointer(), pName, pUrl, pTarget);
6007 SetMapMode(aOld);
6009 return bRet;
6011 return false;
6014 bool ScGridWindow::HasScenarioButton( const Point& rPosPixel, ScRange& rScenRange )
6016 ScDocument& rDoc = mrViewData.GetDocument();
6017 SCTAB nTab = mrViewData.GetTabNo();
6018 SCTAB nTabCount = rDoc.GetTableCount();
6019 if ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) )
6021 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6023 Size aButSize = mrViewData.GetScenButSize();
6024 tools::Long nBWidth = aButSize.Width();
6025 if (!nBWidth)
6026 return false; // No Button drawn yet -> there is none
6027 tools::Long nBHeight = aButSize.Height();
6028 tools::Long nHSpace = static_cast<tools::Long>( SC_SCENARIO_HSPACE * mrViewData.GetPPTX() );
6030 //! cache the Ranges in Table!!!!
6032 ScMarkData aMarks(rDoc.GetSheetLimits());
6033 for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
6034 rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
6035 ScRangeList aRanges;
6036 aMarks.FillRangeListWithMarks( &aRanges, false );
6038 size_t nRangeCount = aRanges.size();
6039 for (size_t j=0; j< nRangeCount; ++j)
6041 ScRange aRange = aRanges[j];
6042 // Always extend scenario frame to merged cells where no new non-covered cells
6043 // are framed
6044 rDoc.ExtendTotalMerge( aRange );
6046 bool bTextBelow = ( aRange.aStart.Row() == 0 );
6048 Point aButtonPos;
6049 if ( bTextBelow )
6051 aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1,
6052 eWhich, true );
6054 else
6056 aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aStart.Row(),
6057 eWhich, true );
6058 aButtonPos.AdjustY( -nBHeight );
6060 if ( bLayoutRTL )
6061 aButtonPos.AdjustX( -(nHSpace - 1) );
6062 else
6063 aButtonPos.AdjustX( -(nBWidth - nHSpace) ); // same for top or bottom
6065 tools::Rectangle aButRect( aButtonPos, Size(nBWidth,nBHeight) );
6066 if ( aButRect.Contains( rPosPixel ) )
6068 rScenRange = aRange;
6069 return true;
6074 return false;
6077 void ScGridWindow::DrawLayerCreated()
6079 SetMapMode( GetDrawMapMode() );
6081 // initially create overlay objects
6082 ImpCreateOverlayObjects();
6085 void ScGridWindow::SetAutoSpellContext( const std::shared_ptr<sc::SpellCheckContext> &ctx )
6087 mpSpellCheckCxt = ctx;
6090 void ScGridWindow::ResetAutoSpell()
6092 if (mpSpellCheckCxt)
6093 mpSpellCheckCxt->reset();
6096 void ScGridWindow::ResetAutoSpellForContentChange()
6098 if (mpSpellCheckCxt)
6099 mpSpellCheckCxt->resetForContentChange();
6102 void ScGridWindow::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges )
6104 if (!mpSpellCheckCxt)
6105 return;
6107 mpSpellCheckCxt->setMisspellRanges(nPosX, nPosY, pRanges);
6110 const std::vector<editeng::MisspellRanges>* ScGridWindow::GetAutoSpellData( SCCOL nPosX, SCROW nPosY )
6112 if (!mpSpellCheckCxt)
6113 return nullptr;
6115 if (!maVisibleRange.isInside(nPosX, nPosY))
6116 return nullptr;
6118 return mpSpellCheckCxt->getMisspellRanges(nPosX, nPosY);
6121 bool ScGridWindow::InsideVisibleRange( SCCOL nPosX, SCROW nPosY )
6123 return maVisibleRange.isInside(nPosX, nPosY);
6126 OString ScGridWindow::getCellCursor() const
6128 // GridWindow stores a shown cell cursor in mpOOCursors, hence
6129 // we can use that to determine whether we would want to be showing
6130 // one (client-side) for tiled rendering too.
6131 if (!mpOOCursors)
6132 return "EMPTY"_ostr;
6134 if (comphelper::LibreOfficeKit::isCompatFlagSet(
6135 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6136 return mrViewData.describeCellCursorInPrintTwips();
6138 return mrViewData.describeCellCursor();
6141 void ScGridWindow::notifyKitCellCursor() const
6143 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6145 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, getCellCursor());
6146 if (bListValButton && aListValPos == mrViewData.GetCurPos())
6147 updateLOKValListButton(true, aListValPos);
6148 std::vector<tools::Rectangle> aRects;
6149 GetSelectionRects(aRects);
6150 if (aRects.empty() || !mrViewData.IsActive())
6152 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
6153 SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
6157 void ScGridWindow::notifyKitCellViewCursor(const SfxViewShell* pForShell) const
6159 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6161 if (pViewShell->GetDocId() != pForShell->GetDocId())
6162 return;
6164 OString aCursor("EMPTY"_ostr);
6165 if (mpOOCursors) // cf. getCellCursor above
6167 auto pForTabView = dynamic_cast<const ScTabViewShell *>(pForShell);
6168 if (!pForTabView)
6169 return;
6171 if (comphelper::LibreOfficeKit::isCompatFlagSet(
6172 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6173 aCursor = mrViewData.describeCellCursorInPrintTwips();
6174 else
6175 aCursor = pForTabView->GetViewData().describeCellCursorAt(
6176 mrViewData.GetCurX(), mrViewData.GetCurY()); // our position.
6178 SfxLokHelper::notifyOtherView(pViewShell, pForShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
6181 // Send our cursor details to a view described by @pForShell, or all views
6182 // if @pForShell is null. In each case send the current view a cell-cursor
6183 // event, and others a cell_view_cursor event.
6185 // NB. we need to re-construct the cursor details for each other view in their
6186 // own zoomed co-ordinate system (but not in scPrintTwipsMsgs mode).
6187 void ScGridWindow::updateKitCellCursor(const SfxViewShell* pForShell) const
6189 if (comphelper::LibreOfficeKit::isCompatFlagSet(
6190 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6192 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6193 // Generate the cursor info string just once and directly send to all.
6194 // Calling notifyKitCellViewCursor() would regenerate the
6195 // cursor-string unnecessarily.
6196 OString aCursor = getCellCursor();
6198 if (pForShell)
6200 SfxLokHelper::notifyOtherView(pViewShell, pForShell,
6201 LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
6203 else
6205 notifyKitCellCursor();
6206 SfxLokHelper::notifyOtherViews(pViewShell,
6207 LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
6210 return;
6213 if (!pForShell)
6215 for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6216 it = SfxViewShell::GetNext(*it))
6217 updateKitCellCursor(it);
6218 return;
6221 if (pForShell == mrViewData.GetViewShell())
6222 notifyKitCellCursor();
6223 else
6224 notifyKitCellViewCursor(pForShell);
6227 void ScGridWindow::updateKitOtherCursors() const
6229 for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6230 it = SfxViewShell::GetNext(*it))
6232 auto pOther = dynamic_cast<const ScTabViewShell *>(it);
6233 if (!pOther)
6234 continue;
6235 const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
6236 assert(pGrid);
6237 if (pGrid == this)
6238 notifyKitCellCursor();
6239 else
6240 pGrid->notifyKitCellViewCursor(mrViewData.GetViewShell());
6244 void ScGridWindow::CursorChanged()
6246 // here the created OverlayObjects may be transformed in later versions. For
6247 // now, just re-create them
6249 UpdateCursorOverlay();
6250 UpdateAutoFillOverlay();
6251 UpdateSparklineGroupOverlay();
6254 void ScGridWindow::ImpCreateOverlayObjects()
6256 UpdateHighlightOverlay();
6257 UpdateSelectionOverlay();
6258 UpdateCursorOverlay();
6259 UpdateAutoFillOverlay();
6260 UpdateCopySourceOverlay();
6261 UpdateDragRectOverlay();
6262 UpdateHeaderOverlay();
6263 UpdateShrinkOverlay();
6264 UpdateSparklineGroupOverlay();
6267 void ScGridWindow::ImpDestroyOverlayObjects()
6269 DeleteCursorOverlay();
6270 DeleteCopySourceOverlay();
6271 DeleteSelectionOverlay();
6272 mpOOHighlight.reset(); // DeleteHighlightOverlay
6273 DeleteAutoFillOverlay();
6274 DeleteDragRectOverlay();
6275 DeleteHeaderOverlay();
6276 DeleteShrinkOverlay();
6277 DeleteSparklineGroupOverlay();
6280 void ScGridWindow::UpdateAllOverlays()
6282 // delete and re-allocate all overlay objects
6284 ImpDestroyOverlayObjects();
6285 ImpCreateOverlayObjects();
6288 void ScGridWindow::DeleteCursorOverlay()
6290 if (comphelper::LibreOfficeKit::isActive() && mrViewData.HasEditView(eWhich))
6291 return;
6292 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6293 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, "EMPTY"_ostr);
6294 SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
6295 mpOOCursors.reset();
6298 void ScGridWindow::DeleteCopySourceOverlay()
6300 mpOOSelectionBorder.reset();
6303 void ScGridWindow::UpdateCopySourceOverlay()
6305 const MapMode aDrawMode = GetDrawMapMode();
6306 const MapMode aOldMode = GetMapMode();
6307 comphelper::ScopeGuard aMapModeGuard(
6308 [&aOldMode, &aDrawMode, this] {
6309 if (aOldMode != aDrawMode)
6310 SetMapMode(aOldMode);
6313 if (aOldMode != aDrawMode)
6314 SetMapMode(aDrawMode);
6316 DeleteCopySourceOverlay();
6318 if (comphelper::LibreOfficeKit::isActive())
6319 return;
6320 if (!mrViewData.ShowPasteSource())
6321 return;
6322 if (!ScModule::get()->GetInputOptions().GetEnterPasteMode())
6323 return;
6324 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6325 if (!xOverlayManager.is())
6326 return;
6327 const ScTransferObj* pTransObj = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(mrViewData.GetActiveWin()));
6328 if (!pTransObj)
6329 return;
6330 ScDocument* pClipDoc = pTransObj->GetDocument();
6331 if (!pClipDoc)
6332 return;
6334 SCTAB nCurTab = mrViewData.GetCurPos().Tab();
6336 ScClipParam& rClipParam = pClipDoc->GetClipParam();
6337 mpOOSelectionBorder.reset(new sdr::overlay::OverlayObjectList);
6338 for ( size_t i = 0; i < rClipParam.maRanges.size(); ++i )
6340 ScRange const & r = rClipParam.maRanges[i];
6341 if (r.aStart.Tab() != nCurTab)
6342 continue;
6344 SCCOL nClipStartX = r.aStart.Col();
6345 SCROW nClipStartY = r.aStart.Row();
6346 SCCOL nClipEndX = r.aEnd.Col();
6347 SCROW nClipEndY = r.aEnd.Row();
6349 Point aClipStartScrPos = mrViewData.GetScrPos( nClipStartX, nClipStartY, eWhich );
6350 Point aClipEndScrPos = mrViewData.GetScrPos( nClipEndX + 1, nClipEndY + 1, eWhich );
6351 aClipStartScrPos -= Point(1, 1);
6352 tools::Long nSizeXPix = aClipEndScrPos.X() - aClipStartScrPos.X();
6353 tools::Long nSizeYPix = aClipEndScrPos.Y() - aClipStartScrPos.Y();
6355 tools::Rectangle aRect( aClipStartScrPos, Size(nSizeXPix, nSizeYPix) );
6357 Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
6359 tools::Rectangle aLogic = PixelToLogic(aRect, aDrawMode);
6360 ::basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aLogic);
6361 std::unique_ptr<ScOverlayDashedBorder> pDashedBorder(new ScOverlayDashedBorder(aRange, aHighlight));
6362 xOverlayManager->add(*pDashedBorder);
6363 mpOOSelectionBorder->append(std::move(pDashedBorder));
6367 static std::vector<tools::Rectangle> convertPixelToLogical(
6368 const ScViewData& rViewData,
6369 const std::vector<tools::Rectangle>& rRectangles,
6370 tools::Rectangle &rBoundingBox)
6372 std::vector<tools::Rectangle> aLogicRects;
6374 double nPPTX = rViewData.GetPPTX();
6375 double nPPTY = rViewData.GetPPTY();
6377 for (const auto& rRectangle : rRectangles)
6379 // We explicitly create a copy, since we need to expand
6380 // the rectangle before coordinate conversion
6381 tools::Rectangle aRectangle(rRectangle);
6382 aRectangle.AdjustRight(1 );
6383 aRectangle.AdjustBottom(1 );
6385 tools::Rectangle aRect(aRectangle.Left() / nPPTX, aRectangle.Top() / nPPTY,
6386 aRectangle.Right() / nPPTX, aRectangle.Bottom() / nPPTY);
6388 rBoundingBox.Union(aRect);
6389 aLogicRects.push_back(aRect);
6391 return aLogicRects;
6394 static OString rectanglesToString(const std::vector<tools::Rectangle> &rLogicRects)
6396 bool bFirst = true;
6397 OStringBuffer aRects;
6398 for (const auto &rRect : rLogicRects)
6400 if (!bFirst)
6401 aRects.append("; ");
6402 bFirst = false;
6403 aRects.append(rRect.toString());
6405 return aRects.makeStringAndClear();
6409 * Turn the selection ranges rRectangles into the LibreOfficeKit selection, and send to other views.
6411 * @param pLogicRects - if set then don't invoke the callback, just collect the rectangles in the pointed vector.
6413 void ScGridWindow::UpdateKitSelection(const std::vector<tools::Rectangle>& rRectangles, std::vector<tools::Rectangle>* pLogicRects)
6415 if (!comphelper::LibreOfficeKit::isActive())
6416 return;
6418 // If this is true, rRectangles should already in print twips.
6419 // If false, rRectangles are in pixels.
6420 bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
6421 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
6423 tools::Rectangle aBoundingBox;
6424 std::vector<tools::Rectangle> aConvertedRects;
6426 if (bInPrintTwips)
6427 std::for_each(rRectangles.begin(), rRectangles.end(),
6428 [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
6429 else
6430 aConvertedRects = convertPixelToLogical(mrViewData, rRectangles, aBoundingBox);
6432 const std::vector<tools::Rectangle>& rLogicRects = bInPrintTwips ? rRectangles : aConvertedRects;
6433 if (pLogicRects)
6435 *pLogicRects = rLogicRects;
6436 return;
6439 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6440 pViewShell->UpdateInputHandler();
6441 OString sBoundingBoxString = "EMPTY"_ostr;
6442 if (!aBoundingBox.IsEmpty())
6443 sBoundingBoxString = aBoundingBox.toString();
6444 OString aRectListString = rectanglesToString(rLogicRects);
6445 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
6446 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectListString);
6448 if (bInPrintTwips)
6450 SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
6451 "selection", aRectListString);
6452 return;
6455 for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6456 it = SfxViewShell::GetNext(*it))
6458 if (it == pViewShell)
6459 continue;
6460 auto pOther = dynamic_cast<const ScTabViewShell *>(it);
6461 if (!pOther)
6462 return;
6464 const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
6465 assert(pGrid);
6467 // Fetch pixels & convert for each view separately.
6468 tools::Rectangle aDummyBBox;
6469 std::vector<tools::Rectangle> aPixelRects;
6470 pGrid->GetPixelRectsFor(mrViewData.GetMarkData() /* ours */, aPixelRects);
6471 auto aOtherLogicRects = convertPixelToLogical(pOther->GetViewData(), aPixelRects, aDummyBBox);
6472 SfxLokHelper::notifyOtherView(pViewShell, pOther, LOK_CALLBACK_TEXT_VIEW_SELECTION,
6473 "selection", rectanglesToString(aOtherLogicRects));
6478 * Fetch the selection ranges for other views into the LibreOfficeKit selection,
6479 * map them into our view co-ordinates and send to our view.
6481 void ScGridWindow::updateOtherKitSelections() const
6483 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6484 bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
6485 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
6487 for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
6488 it = SfxViewShell::GetNext(*it))
6490 auto pOther = dynamic_cast<const ScTabViewShell *>(it);
6491 if (!pOther)
6492 continue;
6494 // Fetch pixels & convert for each view separately.
6495 tools::Rectangle aBoundingBox;
6496 std::vector<tools::Rectangle> aRects;
6497 OString aRectsString;
6498 GetRectsAnyFor(pOther->GetViewData().GetMarkData() /* theirs */, aRects, bInPrintTwips);
6499 if (bInPrintTwips)
6501 std::for_each(aRects.begin(), aRects.end(),
6502 [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
6503 aRectsString = rectanglesToString(aRects);
6505 else
6506 aRectsString = rectanglesToString(
6507 convertPixelToLogical(pViewShell->GetViewData(), aRects, aBoundingBox));
6509 if (it == pViewShell)
6511 OString sBoundingBoxString = "EMPTY"_ostr;
6512 if (!aBoundingBox.IsEmpty())
6513 sBoundingBoxString = aBoundingBox.toString();
6515 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
6516 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
6518 else
6519 SfxLokHelper::notifyOtherView(it, pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
6520 "selection", aRectsString);
6524 namespace
6527 void updateLibreOfficeKitAutoFill(const ScViewData& rViewData, tools::Rectangle const & rRectangle)
6529 if (!comphelper::LibreOfficeKit::isActive())
6530 return;
6532 double nPPTX = rViewData.GetPPTX();
6533 double nPPTY = rViewData.GetPPTY();
6535 OString sRectangleString = "EMPTY"_ostr;
6536 if (!rRectangle.IsEmpty())
6538 // selection start handle
6539 tools::Rectangle aLogicRectangle(
6540 rRectangle.Left() / nPPTX, rRectangle.Top() / nPPTY,
6541 rRectangle.Right() / nPPTX, rRectangle.Bottom() / nPPTY);
6542 sRectangleString = aLogicRectangle.toString();
6545 ScTabViewShell* pViewShell = rViewData.GetViewShell();
6546 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_AUTO_FILL_AREA, sRectangleString);
6549 } //end anonymous namespace
6551 void ScGridWindow::UpdateCursorOverlay()
6553 ScDocument& rDoc = mrViewData.GetDocument();
6555 const MapMode aDrawMode = GetDrawMapMode();
6556 const MapMode aOldMode = GetMapMode();
6557 comphelper::ScopeGuard aMapModeGuard(
6558 [&aOldMode, &aDrawMode, this] {
6559 if (aOldMode != aDrawMode)
6560 SetMapMode(aOldMode);
6563 if (aOldMode != aDrawMode)
6564 SetMapMode(aDrawMode);
6566 // Existing OverlayObjects may be transformed in later versions.
6567 // For now, just re-create them.
6569 DeleteCursorOverlay();
6571 std::vector<tools::Rectangle> aPixelRects;
6573 // determine the cursor rectangles in pixels (moved from ScGridWindow::DrawCursor)
6575 SCTAB nTab = mrViewData.GetTabNo();
6576 SCCOL nX = mrViewData.GetCurX();
6577 SCROW nY = mrViewData.GetCurY();
6579 const ScPatternAttr* pPattern = rDoc.GetPattern(nX,nY,nTab);
6581 if (!comphelper::LibreOfficeKit::isActive() && !maVisibleRange.isInside(nX, nY))
6583 if (maVisibleRange.mnCol2 < nX || maVisibleRange.mnRow2 < nY)
6584 return; // no further check needed, nothing visible
6586 // fdo#87382 Also display the cell cursor for the visible part of
6587 // merged cells if the view position is part of merged cells.
6588 const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
6589 if (rMerge.GetColMerge() <= 1 && rMerge.GetRowMerge() <= 1)
6590 return; // not merged and invisible
6592 SCCOL nX2 = nX + rMerge.GetColMerge() - 1;
6593 SCROW nY2 = nY + rMerge.GetRowMerge() - 1;
6594 // Check if the middle or tail of the merged range is visible.
6595 if (maVisibleRange.mnCol1 > nX2 || maVisibleRange.mnRow1 > nY2)
6596 return; // no visible part
6599 // don't show the cursor in overlapped cells
6600 const ScMergeFlagAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE_FLAG);
6601 bool bOverlapped = rMergeFlag.IsOverlapped();
6603 // left or above of the screen?
6604 bool bVis = comphelper::LibreOfficeKit::isActive() || ( nX>=mrViewData.GetPosX(eHWhich) && nY>=mrViewData.GetPosY(eVWhich) );
6605 if (!bVis)
6607 SCCOL nEndX = nX;
6608 SCROW nEndY = nY;
6609 const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
6610 if (rMerge.GetColMerge() > 1)
6611 nEndX += rMerge.GetColMerge()-1;
6612 if (rMerge.GetRowMerge() > 1)
6613 nEndY += rMerge.GetRowMerge()-1;
6614 bVis = ( nEndX>=mrViewData.GetPosX(eHWhich) && nEndY>=mrViewData.GetPosY(eVWhich) );
6617 if ( bVis && !bOverlapped && !mrViewData.HasEditView(eWhich) && mrViewData.IsActive() )
6619 Point aScrPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
6620 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6622 // completely right of/below the screen?
6623 // (test with logical start position in aScrPos)
6624 bool bMaybeVisible;
6625 if ( bLayoutRTL )
6626 bMaybeVisible = ( aScrPos.X() >= -2 && aScrPos.Y() >= -2 );
6627 else
6629 Size aOutSize = GetOutputSizePixel();
6630 bMaybeVisible = ( aScrPos.X() <= aOutSize.Width() + 2 && aScrPos.Y() <= aOutSize.Height() + 2 );
6633 // in the tiled rendering case, don't limit to the screen size
6634 if (bMaybeVisible || comphelper::LibreOfficeKit::isActive())
6636 tools::Long nSizeXPix;
6637 tools::Long nSizeYPix;
6638 mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
6640 if (bLayoutRTL)
6641 aScrPos.AdjustX( -(nSizeXPix - 2) ); // move instead of mirroring
6643 // show the cursor as 4 (thin) rectangles
6644 tools::Rectangle aRect(aScrPos, Size(nSizeXPix, nSizeYPix));
6646 float fScaleFactor = GetDPIScaleFactor();
6648 tools::Long aCursorWidth = 1 * fScaleFactor;
6650 tools::Rectangle aLeft = aRect;
6651 aLeft.AdjustTop( -aCursorWidth );
6652 aLeft.AdjustBottom(aCursorWidth );
6653 aLeft.SetRight( aLeft.Left() );
6654 aLeft.AdjustLeft( -aCursorWidth );
6656 tools::Rectangle aRight = aRect;
6657 aRight.AdjustTop( -aCursorWidth );
6658 aRight.AdjustBottom(aCursorWidth );
6659 aRight.SetLeft( aRight.Right() );
6660 aRight.AdjustRight(aCursorWidth );
6662 tools::Rectangle aTop = aRect;
6663 aTop.SetBottom( aTop.Top() );
6664 aTop.AdjustTop( -aCursorWidth );
6666 tools::Rectangle aBottom = aRect;
6667 aBottom.SetTop( aBottom.Bottom() );
6668 aBottom.AdjustBottom(aCursorWidth );
6670 aPixelRects.push_back(aLeft);
6671 aPixelRects.push_back(aRight);
6672 aPixelRects.push_back(aTop);
6673 aPixelRects.push_back(aBottom);
6677 if ( !aPixelRects.empty() )
6679 if (comphelper::LibreOfficeKit::isActive())
6681 mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
6682 updateKitCellCursor(nullptr);
6684 else
6686 // #i70788# get the OverlayManager safely
6687 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6689 if (xOverlayManager.is())
6691 ScModule* mod = ScModule::get();
6692 Color aCursorColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
6693 if (mrViewData.GetActivePart() != eWhich)
6694 // non-active pane uses a different color.
6695 aCursorColor = mod->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
6696 std::vector< basegfx::B2DRange > aRanges;
6697 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
6699 // tdf#143733, tdf#145080 - improve border visibility
6700 // constants picked for maximum consistency at 100% and adequate response on zoom
6701 // line width = 1.5 at 100% (0.75 left +/- 0.75 right), 50% = 1, 200% = 1.25, 400% = 2.25
6702 const double fCurZoom(mrViewData.GetZoomX());
6703 const double fMinSize = 0.25 * GetDPIScaleFactor();
6704 const double fAdjust(fMinSize + mrViewData.GetZoomX() * 0.5);
6705 int nAdjustPixel(o3tl::convert(fAdjust, o3tl::Length::pt, o3tl::Length::px));
6706 // If zoom level is 50% or greater the rectangles must be at least 1 pixel thick
6707 if (fCurZoom >= 0.5)
6708 nAdjustPixel = std::max(1, nAdjustPixel);
6710 // Below each rectangle is adjusted so that they have thickness of nAdjustPixel
6711 // Left rectangle
6712 basegfx::B2DRange aRBLeft(aPixelRects[0].Left() - nAdjustPixel, aPixelRects[0].Top() - nAdjustPixel,
6713 aPixelRects[0].Right(), aPixelRects[0].Bottom() + nAdjustPixel);
6714 aRBLeft.transform(aTransform);
6715 aRanges.push_back(aRBLeft);
6716 // Right rectangle
6717 basegfx::B2DRange aRBRight(aPixelRects[1].Left(), aPixelRects[1].Top() - nAdjustPixel,
6718 aPixelRects[1].Right() + nAdjustPixel, aPixelRects[1].Bottom() + nAdjustPixel);
6719 aRBRight.transform(aTransform);
6720 aRanges.push_back(aRBRight);
6721 // Top rectangle
6722 basegfx::B2DRange aRBTop(aPixelRects[2].Left() - nAdjustPixel, aPixelRects[2].Top() - nAdjustPixel,
6723 aPixelRects[2].Right() + nAdjustPixel, aPixelRects[2].Bottom());
6724 aRBTop.transform(aTransform);
6725 aRanges.push_back(aRBTop);
6726 // Bottom rectangle
6727 basegfx::B2DRange aRBBottom(aPixelRects[3].Left() - nAdjustPixel, aPixelRects[3].Top(),
6728 aPixelRects[3].Right() + nAdjustPixel, aPixelRects[3].Bottom() + nAdjustPixel);
6729 aRBBottom.transform(aTransform);
6730 aRanges.push_back(aRBBottom);
6732 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
6733 sdr::overlay::OverlayType::Solid,
6734 aCursorColor,
6735 std::move(aRanges),
6736 false, false));
6738 // Internal white contrast rectangle
6739 std::vector< basegfx::B2DRange > aRangesInternal;
6740 basegfx::B2DRange aRBInternal(aPixelRects[0].Right(), aPixelRects[2].Bottom(),
6741 aPixelRects[1].Left() - 1, aPixelRects[3].Top() - 1);
6742 aRBInternal.transform(aTransform);
6743 aRangesInternal.push_back(aRBInternal);
6745 std::unique_ptr<sdr::overlay::OverlayObject> pOverlayInternal(new sdr::overlay::OverlaySelection(
6746 sdr::overlay::OverlayType::NoFill,
6747 COL_WHITE,
6748 std::move(aRangesInternal),
6749 true, false));
6751 xOverlayManager->add(*pOverlay);
6752 xOverlayManager->add(*pOverlayInternal);
6753 mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
6754 mpOOCursors->append(std::move(pOverlay));
6755 mpOOCursors->append(std::move(pOverlayInternal));
6761 void ScGridWindow::GetCellSelection(std::vector<tools::Rectangle>& rLogicRects)
6763 std::vector<tools::Rectangle> aRects;
6764 if (comphelper::LibreOfficeKit::isActive() &&
6765 comphelper::LibreOfficeKit::isCompatFlagSet(
6766 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6767 GetSelectionRectsPrintTwips(aRects);
6768 else
6769 GetSelectionRects(aRects);
6770 UpdateKitSelection(aRects, &rLogicRects);
6773 void ScGridWindow::DeleteSelectionOverlay()
6775 mpOOSelection.reset();
6778 void ScGridWindow::UpdateSelectionOverlay()
6780 const MapMode aDrawMode = GetDrawMapMode();
6781 const MapMode aOldMode = GetMapMode();
6782 comphelper::ScopeGuard aMapModeGuard(
6783 [&aOldMode, &aDrawMode, this] {
6784 if (aOldMode != aDrawMode)
6785 SetMapMode(aOldMode);
6788 if (aOldMode != aDrawMode)
6789 SetMapMode(aDrawMode);
6791 DeleteSelectionOverlay();
6792 std::vector<tools::Rectangle> aRects;
6793 if (comphelper::LibreOfficeKit::isActive() &&
6794 comphelper::LibreOfficeKit::isCompatFlagSet(
6795 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6796 GetSelectionRectsPrintTwips(aRects);
6797 else
6798 GetSelectionRects(aRects);
6800 if (!aRects.empty() && mrViewData.IsActive())
6802 // #i70788# get the OverlayManager safely
6803 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6804 if (comphelper::LibreOfficeKit::isActive())
6806 // notify the LibreOfficeKit too
6807 UpdateKitSelection(aRects);
6809 else if (xOverlayManager.is())
6811 std::vector< basegfx::B2DRange > aRanges;
6812 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
6813 ScDocument& rDoc = mrViewData.GetDocument();
6814 SCTAB nTab = mrViewData.GetTabNo();
6815 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6817 for(const tools::Rectangle & rRA : aRects)
6819 if (bLayoutRTL)
6821 basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
6822 aRB.transform(aTransform);
6823 aRanges.push_back(aRB);
6825 else
6827 basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
6828 aRB.transform(aTransform);
6829 aRanges.push_back(aRB);
6833 // get the system's highlight color
6834 const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
6836 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
6837 sdr::overlay::OverlayType::Transparent,
6838 aHighlight,
6839 std::move(aRanges),
6840 true, true));
6842 xOverlayManager->add(*pOverlay);
6843 mpOOSelection.reset(new sdr::overlay::OverlayObjectList);
6844 mpOOSelection->append(std::move(pOverlay));
6847 else
6849 ScTabViewShell* pViewShell = mrViewData.GetViewShell();
6850 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY"_ostr);
6851 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY"_ostr);
6852 SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
6854 ScInputHandler* pViewHdl = ScModule::get()->GetInputHdl(pViewShell);
6855 if (!pViewHdl || !pViewHdl->IsEditMode())
6857 std::vector<ReferenceMark> aReferenceMarks;
6858 ScInputHandler::SendReferenceMarks(pViewShell, aReferenceMarks);
6863 void ScGridWindow::UpdateHighlightOverlay()
6865 mpOOHighlight.reset(); // DeleteHighlightOverlay
6866 std::vector<tools::Rectangle> aRects;
6867 if (comphelper::LibreOfficeKit::isActive() &&
6868 comphelper::LibreOfficeKit::isCompatFlagSet(
6869 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
6870 GetRectsAnyFor(mrViewData.GetHighlightData(), aRects, true);
6871 else
6872 GetPixelRectsFor(mrViewData.GetHighlightData(), aRects);
6874 if (!aRects.empty() && mrViewData.IsActive())
6876 // #i70788# get the OverlayManager safely
6877 if (rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager())
6879 std::vector< basegfx::B2DRange > aRanges;
6880 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
6881 ScDocument& rDoc = mrViewData.GetDocument();
6882 SCTAB nTab = mrViewData.GetTabNo();
6883 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6885 for(const tools::Rectangle & rRA : aRects)
6887 if (bLayoutRTL)
6889 basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
6890 aRB.transform(aTransform);
6891 aRanges.push_back(aRB);
6893 else
6895 basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
6896 aRB.transform(aTransform);
6897 aRanges.push_back(aRB);
6901 ScModule* mod = ScModule::get();
6902 const Color aBackgroundColor = mod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
6903 Color aHighlightColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
6904 aHighlightColor.Merge(aBackgroundColor, 100);
6906 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
6907 sdr::overlay::OverlayType::Transparent,
6908 aHighlightColor,
6909 std::move(aRanges),
6910 false, false));
6912 xOverlayManager->add(*pOverlay);
6913 mpOOHighlight.reset(new sdr::overlay::OverlayObjectList);
6914 mpOOHighlight->append(std::move(pOverlay));
6919 void ScGridWindow::DeleteAutoFillOverlay()
6921 mpOOAutoFill.reset();
6922 mpAutoFillRect.reset();
6925 void ScGridWindow::UpdateAutoFillOverlay()
6927 const MapMode aDrawMode = GetDrawMapMode();
6928 const MapMode aOldMode = GetMapMode();
6929 comphelper::ScopeGuard aMapModeGuard(
6930 [&aOldMode, &aDrawMode, this] {
6931 if (aOldMode != aDrawMode)
6932 SetMapMode(aOldMode);
6935 if (aOldMode != aDrawMode)
6936 SetMapMode(aDrawMode);
6938 DeleteAutoFillOverlay();
6940 // get the AutoFill handle rectangle in pixels
6942 if ( !(bAutoMarkVisible && aAutoMarkPos.Tab() == mrViewData.GetTabNo() &&
6943 !mrViewData.HasEditView(eWhich) && mrViewData.IsActive()) )
6944 return;
6946 SCCOL nX = aAutoMarkPos.Col();
6947 SCROW nY = aAutoMarkPos.Row();
6949 if (!maVisibleRange.isInside(nX, nY) && !comphelper::LibreOfficeKit::isActive())
6951 // Autofill mark is not visible. Bail out.
6952 return;
6955 SCTAB nTab = mrViewData.GetTabNo();
6956 ScDocument& rDoc = mrViewData.GetDocument();
6957 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
6959 // tdf#162006 Ensures the AutoFill handle is visible at any zoom level
6960 // At 100% = Total Size 8 (2px for the external white line; 6px for the handle itself)
6961 const double fScaleFactor(2 + 2 * GetDPIScaleFactor());
6962 const double fZoom(2 + 2 * mrViewData.GetZoomX());
6963 Size aFillHandleSize(fScaleFactor + fZoom, fScaleFactor + fZoom);
6965 Point aFillPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
6966 tools::Long nSizeXPix;
6967 tools::Long nSizeYPix;
6968 mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
6970 // Consider the case of merged cells to determine where to place the AutoFill handle
6971 SCCOL nX2 = mrViewData.GetCurX();
6972 SCCOL nY2 = mrViewData.GetCurY();
6973 const ScMergeAttr* pMerge = rDoc.GetAttr(nX2, nY2, nTab, ATTR_MERGE);
6974 if (pMerge->GetColMerge() >= 1 || pMerge->GetRowMerge() >= 1)
6976 nX2 += pMerge->GetColMerge() - 1;
6977 nY2 += pMerge->GetRowMerge() - 1;
6980 if (bLayoutRTL && !comphelper::LibreOfficeKit::isActive())
6981 aFillPos.AdjustX( -(nSizeXPix + (aFillHandleSize.Width() / 2)) );
6982 else
6983 aFillPos.AdjustX(nSizeXPix - (aFillHandleSize.Width() / 2) );
6985 aFillPos.AdjustY(nSizeYPix);
6986 aFillPos.AdjustY( -(aFillHandleSize.Height() / 2) );
6988 tools::Rectangle aFillRect(aFillPos, aFillHandleSize);
6990 // expand rect to increase hit area
6991 mpAutoFillRect = aFillRect;
6992 mpAutoFillRect->expand(fScaleFactor);
6994 // #i70788# get the OverlayManager safely
6995 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
6996 if (comphelper::LibreOfficeKit::isActive()) // notify the LibreOfficeKit
6998 updateLibreOfficeKitAutoFill(mrViewData, aFillRect);
7000 else if (xOverlayManager.is())
7002 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7004 // Outer rectangle (always white for contrast)
7005 std::vector< basegfx::B2DRange > aRangesOuter;
7006 basegfx::B2DRange aRBOuter = vcl::unotools::b2DRectangleFromRectangle(aFillRect);
7007 aRBOuter.transform(aTransform);
7008 aRangesOuter.push_back(aRBOuter);
7010 std::unique_ptr<sdr::overlay::OverlayObject> pOverlayOuter(new sdr::overlay::OverlaySelection(
7011 sdr::overlay::OverlayType::Solid,
7012 COL_WHITE,
7013 std::move(aRangesOuter),
7014 false, false));
7016 // Inner rectangle
7017 std::vector< basegfx::B2DRange > aRangesInner;
7018 tools::Rectangle aRectInner(aFillRect);
7019 aRectInner.AdjustTop(1);
7020 aRectInner.AdjustBottom(-1);
7021 aRectInner.AdjustLeft(1);
7022 aRectInner.AdjustRight(-1);
7023 basegfx::B2DRange aRBInner = vcl::unotools::b2DRectangleFromRectangle(aRectInner);
7024 aRBInner.transform(aTransform);
7025 aRangesInner.push_back(aRBInner);
7027 ScModule* mod = ScModule::get();
7028 Color aHandleColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
7029 if (mrViewData.GetActivePart() != eWhich)
7030 // non-active pane uses a different color.
7031 aHandleColor = mod->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
7033 std::unique_ptr<sdr::overlay::OverlayObject> pOverlayInner(new sdr::overlay::OverlaySelection(
7034 sdr::overlay::OverlayType::Solid,
7035 aHandleColor,
7036 std::move(aRangesInner),
7037 false, false));
7039 xOverlayManager->add(*pOverlayOuter);
7040 xOverlayManager->add(*pOverlayInner);
7041 mpOOAutoFill.reset(new sdr::overlay::OverlayObjectList);
7042 mpOOAutoFill->append(std::move(pOverlayOuter));
7043 mpOOAutoFill->append(std::move(pOverlayInner));
7047 void ScGridWindow::DeleteDragRectOverlay()
7049 mpOODragRect.reset();
7052 void ScGridWindow::UpdateDragRectOverlay()
7054 bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
7055 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
7057 const MapMode aDrawMode = GetDrawMapMode();
7058 const MapMode aOldMode = GetMapMode();
7059 comphelper::ScopeGuard aMapModeGuard(
7060 [&aOldMode, &aDrawMode, this] {
7061 if (aOldMode != aDrawMode)
7062 SetMapMode(aOldMode);
7065 if (aOldMode != aDrawMode)
7066 SetMapMode(aDrawMode);
7068 DeleteDragRectOverlay();
7070 // get the rectangles in pixels (moved from DrawDragRect)
7072 if ( bDragRect || bPagebreakDrawn )
7074 std::vector<tools::Rectangle> aPixelRects;
7076 SCCOL nX1 = bDragRect ? nDragStartX : aPagebreakDrag.aStart.Col();
7077 SCROW nY1 = bDragRect ? nDragStartY : aPagebreakDrag.aStart.Row();
7078 SCCOL nX2 = bDragRect ? nDragEndX : aPagebreakDrag.aEnd.Col();
7079 SCROW nY2 = bDragRect ? nDragEndY : aPagebreakDrag.aEnd.Row();
7081 SCTAB nTab = mrViewData.GetTabNo();
7083 SCCOL nPosX = mrViewData.GetPosX(WhichH(eWhich));
7084 SCROW nPosY = mrViewData.GetPosY(WhichV(eWhich));
7085 if (nX1 < nPosX) nX1 = nPosX;
7086 if (nX2 < nPosX) nX2 = nPosX;
7087 if (nY1 < nPosY) nY1 = nPosY;
7088 if (nY2 < nPosY) nY2 = nPosY;
7090 Point aScrPos(bInPrintTwips ? mrViewData.GetPrintTwipsPos( nX1, nY1 )
7091 : mrViewData.GetScrPos( nX1, nY1, eWhich ) );
7093 tools::Long nSizeXPix=0;
7094 tools::Long nSizeYPix=0;
7095 ScDocument& rDoc = mrViewData.GetDocument();
7096 double nPPTX = mrViewData.GetPPTX();
7097 double nPPTY = mrViewData.GetPPTY();
7098 SCCOLROW i;
7100 bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
7101 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
7102 tools::Long nAdjust = comphelper::LibreOfficeKit::isActive() ? 0 : 2;
7104 if (rDoc.ValidCol(nX2) && nX2>=nX1)
7105 for (i=nX1; i<=nX2; i++)
7107 tools::Long nWidth = rDoc.GetColWidth( static_cast<SCCOL>(i), nTab );
7108 nSizeXPix += bInPrintTwips ? nWidth : ScViewData::ToPixel( nWidth, nPPTX );
7110 else
7112 aScrPos.AdjustX( -nLayoutSign );
7113 nSizeXPix += nAdjust;
7116 if (rDoc.ValidRow(nY2) && nY2>=nY1)
7117 for (i=nY1; i<=nY2; i++)
7119 tools::Long nHeight = rDoc.GetRowHeight( i, nTab );
7120 nSizeYPix += bInPrintTwips ? nHeight : ScViewData::ToPixel( nHeight, nPPTY );
7122 else
7124 aScrPos.AdjustY( -1 );
7125 nSizeYPix += nAdjust;
7128 if (comphelper::LibreOfficeKit::isActive())
7130 nSizeXPix -= 2;
7131 nSizeYPix -= 2;
7134 aScrPos.AdjustX( -(nAdjust * nLayoutSign) );
7135 aScrPos.AdjustY( -1 * nAdjust );
7136 tools::Rectangle aRect( aScrPos.X(), aScrPos.Y(),
7137 aScrPos.X() + ( nSizeXPix + nAdjust ) * nLayoutSign, aScrPos.Y() + nSizeYPix + nAdjust );
7138 if ( bLayoutRTL )
7140 aRect.SetLeft( aRect.Right() ); // end position is left
7141 aRect.SetRight( aScrPos.X() );
7144 if ( meDragInsertMode == INS_CELLSDOWN )
7146 aPixelRects.emplace_back( aRect.Left()+1, aRect.Top()+3, aRect.Left()+1, aRect.Bottom()-2 );
7147 aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+3, aRect.Right()-1, aRect.Bottom()-2 );
7148 aPixelRects.emplace_back( aRect.Left()+1, aRect.Top(), aRect.Right()-1, aRect.Top()+2 );
7149 aPixelRects.emplace_back( aRect.Left()+1, aRect.Bottom()-1, aRect.Right()-1, aRect.Bottom()-1 );
7151 else if ( meDragInsertMode == INS_CELLSRIGHT )
7153 aPixelRects.emplace_back( aRect.Left(), aRect.Top()+1, aRect.Left()+2, aRect.Bottom()-1 );
7154 aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+1, aRect.Right()-1, aRect.Bottom()-1 );
7155 aPixelRects.emplace_back( aRect.Left()+3, aRect.Top()+1, aRect.Right()-2, aRect.Top()+1 );
7156 aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-1, aRect.Right()-2, aRect.Bottom()-1 );
7158 else
7160 aPixelRects.emplace_back( aRect.Left(), aRect.Top(), aRect.Left()+2, aRect.Bottom() );
7161 aPixelRects.emplace_back( aRect.Right()-2, aRect.Top(), aRect.Right(), aRect.Bottom() );
7162 aPixelRects.emplace_back( aRect.Left()+3, aRect.Top(), aRect.Right()-3, aRect.Top()+2 );
7163 aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-2, aRect.Right()-3, aRect.Bottom() );
7166 // #i70788# get the OverlayManager safely
7167 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7169 if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
7171 std::vector< basegfx::B2DRange > aRanges;
7172 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7174 for(const tools::Rectangle & rRA : aPixelRects)
7176 basegfx::B2DRange aRB(rRA.Left(), rRA.Top(), rRA.Right() + 1, rRA.Bottom() + 1);
7177 aRB.transform(aTransform);
7178 aRanges.push_back(aRB);
7181 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7182 sdr::overlay::OverlayType::Invert,
7183 COL_BLACK,
7184 std::move(aRanges),
7185 false, false));
7187 xOverlayManager->add(*pOverlay);
7188 mpOODragRect.reset(new sdr::overlay::OverlayObjectList);
7189 mpOODragRect->append(std::move(pOverlay));
7192 ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
7193 if (comphelper::LibreOfficeKit::isActive() && pViewShell)
7195 OString aRectsString;
7196 tools::Rectangle aBoundingBox;
7198 std::vector<tools::Rectangle> aRectangles;
7199 aRectangles.push_back(aRect);
7201 if (bInPrintTwips)
7203 aBoundingBox = aRect;
7204 aRectsString = rectanglesToString(aRectangles);
7206 else
7208 aRectsString = rectanglesToString(convertPixelToLogical(pViewShell->GetViewData(), aRectangles, aBoundingBox));
7211 OString sBoundingBoxString = "EMPTY"_ostr;
7212 if (!aBoundingBox.IsEmpty())
7213 sBoundingBoxString = aBoundingBox.toString();
7215 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
7216 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
7221 void ScGridWindow::DeleteHeaderOverlay()
7223 mpOOHeader.reset();
7226 void ScGridWindow::UpdateHeaderOverlay()
7228 const MapMode aDrawMode = GetDrawMapMode();
7229 const MapMode aOldMode = GetMapMode();
7230 comphelper::ScopeGuard aMapModeGuard(
7231 [&aOldMode, &aDrawMode, this] {
7232 if (aOldMode != aDrawMode)
7233 SetMapMode(aOldMode);
7236 if (aOldMode != aDrawMode)
7237 SetMapMode(aDrawMode);
7239 DeleteHeaderOverlay();
7241 // Pixel rectangle is in aInvertRect
7242 if ( !aInvertRect.IsEmpty() )
7244 // #i70788# get the OverlayManager safely
7245 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7247 if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
7249 // Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
7250 std::vector< basegfx::B2DRange > aRanges;
7251 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7252 basegfx::B2DRange aRB(aInvertRect.Left(), aInvertRect.Top(), aInvertRect.Right() + 1, aInvertRect.Bottom() + 1);
7254 aRB.transform(aTransform);
7255 aRanges.push_back(aRB);
7257 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7258 sdr::overlay::OverlayType::Invert,
7259 COL_BLACK,
7260 std::move(aRanges),
7261 false, false));
7263 xOverlayManager->add(*pOverlay);
7264 mpOOHeader.reset(new sdr::overlay::OverlayObjectList);
7265 mpOOHeader->append(std::move(pOverlay));
7270 void ScGridWindow::DeleteShrinkOverlay()
7272 mpOOShrink.reset();
7275 void ScGridWindow::UpdateShrinkOverlay()
7277 const MapMode aDrawMode = GetDrawMapMode();
7278 const MapMode aOldMode = GetMapMode();
7279 comphelper::ScopeGuard aMapModeGuard(
7280 [&aOldMode, &aDrawMode, this] {
7281 if (aOldMode != aDrawMode)
7282 SetMapMode(aOldMode);
7285 if (aOldMode != aDrawMode)
7286 SetMapMode(aDrawMode);
7288 DeleteShrinkOverlay();
7290 // get the rectangle in pixels
7292 tools::Rectangle aPixRect;
7293 ScRange aRange;
7294 SCTAB nTab = mrViewData.GetTabNo();
7295 if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() &&
7296 mrViewData.GetDelMark( aRange ) )
7298 //! limit to visible area
7299 if ( aRange.aStart.Col() <= aRange.aEnd.Col() &&
7300 aRange.aStart.Row() <= aRange.aEnd.Row() )
7302 Point aStart = mrViewData.GetScrPos( aRange.aStart.Col(),
7303 aRange.aStart.Row(), eWhich );
7304 Point aEnd = mrViewData.GetScrPos( aRange.aEnd.Col()+1,
7305 aRange.aEnd.Row()+1, eWhich );
7306 aEnd.AdjustX( -1 );
7307 aEnd.AdjustY( -1 );
7309 aPixRect = tools::Rectangle( aStart,aEnd );
7313 if ( !aPixRect.IsEmpty() )
7315 // #i70788# get the OverlayManager safely
7316 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7318 if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
7320 std::vector< basegfx::B2DRange > aRanges;
7321 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7322 basegfx::B2DRange aRB(aPixRect.Left(), aPixRect.Top(), aPixRect.Right() + 1, aPixRect.Bottom() + 1);
7324 aRB.transform(aTransform);
7325 aRanges.push_back(aRB);
7327 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7328 sdr::overlay::OverlayType::Invert,
7329 COL_BLACK,
7330 std::move(aRanges),
7331 false, false));
7333 xOverlayManager->add(*pOverlay);
7334 mpOOShrink.reset(new sdr::overlay::OverlayObjectList);
7335 mpOOShrink->append(std::move(pOverlay));
7340 void ScGridWindow::DeleteSparklineGroupOverlay()
7342 mpOOSparklineGroup.reset();
7345 void ScGridWindow::UpdateSparklineGroupOverlay()
7347 const MapMode aDrawMode = GetDrawMapMode();
7348 const MapMode aOldMode = GetMapMode();
7349 comphelper::ScopeGuard aMapModeGuard(
7350 [&aOldMode, &aDrawMode, this] {
7351 if (aOldMode != aDrawMode)
7352 SetMapMode(aOldMode);
7355 if (aOldMode != aDrawMode)
7356 SetMapMode(aDrawMode);
7358 DeleteSparklineGroupOverlay();
7360 ScAddress aCurrentAddress = mrViewData.GetCurPos();
7362 ScDocument& rDocument = mrViewData.GetDocument();
7363 if (auto pSparkline = rDocument.GetSparkline(aCurrentAddress))
7365 mpOOSparklineGroup.reset(new sdr::overlay::OverlayObjectList);
7367 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7368 if (xOverlayManager.is())
7370 auto* pList = rDocument.GetSparklineList(aCurrentAddress.Tab());
7371 if (pList)
7373 auto const aSparklines = pList->getSparklinesFor(pSparkline->getSparklineGroup());
7375 Color aColor = SvtOptionsDrawinglayer::getHilightColor();
7377 std::vector<basegfx::B2DRange> aRanges;
7378 const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
7380 for (auto const& pCurrentSparkline : aSparklines)
7382 SCCOL nColumn = pCurrentSparkline->getColumn();
7383 SCROW nRow = pCurrentSparkline->getRow();
7385 Point aStart = mrViewData.GetScrPos(nColumn, nRow, eWhich);
7386 Point aEnd = mrViewData.GetScrPos(nColumn + 1, nRow + 1, eWhich);
7388 basegfx::B2DRange aRange(aStart.X(), aStart.Y(), aEnd.X(), aEnd.Y());
7390 aRange.transform(aTransform);
7391 aRanges.push_back(aRange);
7394 std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
7395 sdr::overlay::OverlayType::Transparent,
7396 aColor, std::move(aRanges), true, false));
7398 xOverlayManager->add(*pOverlay);
7399 mpOOSparklineGroup->append(std::move(pOverlay));
7405 // #i70788# central method to get the OverlayManager safely
7406 rtl::Reference<sdr::overlay::OverlayManager> ScGridWindow::getOverlayManager() const
7408 SdrPageView* pPV = mrViewData.GetView()->GetScDrawView()->GetSdrPageView();
7410 if(pPV)
7412 SdrPageWindow* pPageWin = pPV->FindPageWindow( *GetOutDev() );
7414 if ( pPageWin )
7416 return pPageWin->GetOverlayManager();
7420 return rtl::Reference<sdr::overlay::OverlayManager>();
7423 void ScGridWindow::flushOverlayManager()
7425 // #i70788# get the OverlayManager safely
7426 rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
7428 if (xOverlayManager.is())
7429 xOverlayManager->flush();
7432 ScViewData& ScGridWindow::getViewData()
7434 return mrViewData;
7437 FactoryFunction ScGridWindow::GetUITestFactory() const
7439 return ScGridWinUIObject::create;
7442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */