LibreOffice Module sc (master)  1
gridwin.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <scitems.hxx>
21 
22 #include <cstdlib>
23 #include <memory>
24 #include <editeng/adjustitem.hxx>
25 #include <sot/storage.hxx>
26 #include <editeng/eeitem.hxx>
27 #include <editeng/editview.hxx>
28 #include <editeng/editstat.hxx>
29 #include <editeng/flditem.hxx>
30 #include <editeng/justifyitem.hxx>
32 #include <editeng/editobj.hxx>
33 #include <o3tl/unit_conversion.hxx>
34 #include <sfx2/dispatch.hxx>
35 #include <sfx2/viewfrm.hxx>
36 #include <sfx2/docfile.hxx>
37 #include <sfx2/ipclient.hxx>
38 #include <svl/stritem.hxx>
39 #include <svl/sharedstringpool.hxx>
40 #include <vcl/canvastools.hxx>
41 #include <vcl/commandevent.hxx>
42 #include <vcl/cursor.hxx>
43 #include <vcl/inputctx.hxx>
44 #include <vcl/menu.hxx>
45 #include <vcl/settings.hxx>
46 #include <vcl/weldutils.hxx>
47 #include <sot/formats.hxx>
48 #include <comphelper/classids.hxx>
49 
50 #include <svx/svdview.hxx>
51 #include <editeng/outliner.hxx>
52 #include <svx/svdocapt.hxx>
53 #include <svx/svdpagv.hxx>
55 
56 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
57 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
58 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
59 #include <com/sun/star/sheet/MemberResultFlags.hpp>
60 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
61 #include <com/sun/star/awt/KeyModifier.hpp>
62 #include <com/sun/star/awt/MouseButton.hpp>
63 #include <com/sun/star/script/vba/VBAEventId.hpp>
64 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
65 #include <com/sun/star/text/textfield/Type.hpp>
66 
67 #include <gridwin.hxx>
68 #include <tabvwsh.hxx>
69 #include <docsh.hxx>
70 #include <viewdata.hxx>
71 #include <tabview.hxx>
72 #include <select.hxx>
73 #include <scmod.hxx>
74 #include <document.hxx>
75 #include <attrib.hxx>
76 #include <dbdata.hxx>
77 #include <stlpool.hxx>
78 #include <printfun.hxx>
79 #include <cbutton.hxx>
80 #include <sc.hrc>
81 #include <helpids.h>
82 #include <globstr.hrc>
83 #include <strings.hrc>
84 #include <editutil.hxx>
85 #include <scresid.hxx>
86 #include <inputhdl.hxx>
87 #include <uiitems.hxx>
88 #include <formulacell.hxx>
89 #include <patattr.hxx>
90 #include <notemark.hxx>
91 #include <rfindlst.hxx>
92 #include <output.hxx>
93 #include <docfunc.hxx>
94 #include <dbdocfun.hxx>
95 #include <dpobject.hxx>
96 #include <transobj.hxx>
97 #include <drwtrans.hxx>
98 #include <seltrans.hxx>
99 #include <sizedev.hxx>
100 #include <AccessibilityHints.hxx>
101 #include <dpsave.hxx>
102 #include <viewuno.hxx>
103 #include <compiler.hxx>
104 #include <editable.hxx>
105 #include <fillinfo.hxx>
106 #include <filterentries.hxx>
107 #include <drwlayer.hxx>
108 #include <validat.hxx>
109 #include <tabprotection.hxx>
110 #include <postit.hxx>
111 #include <dpcontrol.hxx>
112 #include <checklistmenu.hxx>
113 #include <clipparam.hxx>
114 #include <overlayobject.hxx>
115 #include <cellsuno.hxx>
116 #include <drawview.hxx>
117 #include <dragdata.hxx>
118 #include <cliputil.hxx>
119 #include <queryentry.hxx>
120 #include <markdata.hxx>
121 #include <externalrefmgr.hxx>
122 #include <spellcheckcontext.hxx>
123 #include <uiobject.hxx>
124 #include <undoblk.hxx>
125 #include <datamapper.hxx>
126 #include <inputopt.hxx>
127 #include <queryparam.hxx>
128 
129 #include <svx/sdrpagewindow.hxx>
131 #include <vcl/svapp.hxx>
132 #include <vcl/uitest/logger.hxx>
135 #include <comphelper/lok.hxx>
136 #include <sfx2/lokhelper.hxx>
137 
138 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
139 
140 #include <vector>
141 #include <boost/property_tree/json_parser.hpp>
142 
143 #include <FilterListBox.hxx>
144 
145 using namespace css;
146 using namespace css::uno;
147 
149 {
151 
153  mbActivatePart(false)
154  {}
155 };
156 
157 #define SC_FILTERLISTBOX_LINES 12
158 
160  : mnCol1(0)
161  , mnCol2(rDoc.MaxCol())
162  , mnRow1(0)
163  , mnRow2(rDoc.MaxRow())
164 {
165 }
166 
168 {
169  return mnCol1 <= nCol && nCol <= mnCol2 && mnRow1 <= nRow && nRow <= mnRow2;
170 }
171 
172 bool ScGridWindow::VisibleRange::set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
173 {
174  bool bChanged = mnCol1 != nCol1 || mnRow1 != nRow1 || mnCol2 != nCol2 || mnRow2 != nRow2;
175 
176  mnCol1 = nCol1;
177  mnRow1 = nRow1;
178  mnCol2 = nCol2;
179  mnRow2 = nRow2;
180 
181  return bChanged;
182 }
183 
184 // ListBox in a FloatingWindow (pParent)
186  SCCOL nNewCol, SCROW nNewRow, ScFilterBoxMode eNewMode)
187  : xBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterlist.ui"))
188  , xPopover(xBuilder->weld_popover("FilterList"))
189  , xTreeView(xBuilder->weld_tree_view("list"))
190  , pGridWin(pGrid)
191  , nCol(nNewCol)
192  , nRow(nNewRow)
193  , bInit(true)
194  , bCancelled(false)
195  , bGridHadMouseCaptured(pGrid->IsMouseCaptured())
196  , nSel(0)
197  , eMode(eNewMode)
198  , nAsyncSelectHdl(nullptr)
199 {
200  xTreeView->connect_row_activated(LINK(this, ScFilterListBox, SelectHdl));
201  xTreeView->connect_key_press(LINK(this, ScFilterListBox, KeyInputHdl));
202 }
203 
205 {
206  if (nAsyncSelectHdl)
207  {
209  nAsyncSelectHdl = nullptr;
210  }
211 }
212 
214 {
215  sal_Int32 nPos = xTreeView->get_selected_index();
216  if (nPos == -1)
217  nSel = 0;
218  else
219  nSel = nPos;
220 
221  bInit = false;
222 }
223 
224 IMPL_LINK(ScFilterListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
225 {
226  bool bDone = false;
227 
228  vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
229  // esc with no modifiers
230  if (!aCode.GetModifier() && aCode.GetCode() == KEY_ESCAPE)
231  {
232  pGridWin->ClickExtern(); // clears the listbox
233  bDone = true;
234  }
235 
236  // nowhere to tab to
237  if (aCode.GetCode() == KEY_TAB)
238  bDone = true;
239 
240  return bDone;
241 }
242 
244 {
245  if (!bInit && !bCancelled && !nAsyncSelectHdl)
246  {
247  int nPos = xTreeView->get_selected_index();
248  if (nPos != -1)
249  {
250  nSel = nPos;
251  // #i81298# launch async so the box isn't deleted from modifications within FilterSelect
252  nAsyncSelectHdl = Application::PostUserEvent(LINK(this, ScFilterListBox, AsyncSelectHdl));
253  }
254  }
255  return true;
256 }
257 
258 IMPL_LINK_NOARG(ScFilterListBox, AsyncSelectHdl, void*, void)
259 {
260  nAsyncSelectHdl = nullptr;
261 
262  //tdf#133971 hold self-ref until we return
263  auto xThis(shared_from_this());
264  pGridWin->FilterSelect(nSel);
265  if (xThis.use_count() == 1)
266  {
267  // tdf#133855 we got disposed by FilterSelect
268  return;
269  }
270  pGridWin->ClickExtern();
271 }
272 
273 static bool lcl_IsEditableMatrix( ScDocument& rDoc, const ScRange& rRange )
274 {
275  // If it is an editable range and if there is a Matrix cell at the bottom right with an
276  // origin top left then the range will be set to contain the exact matrix.
278  if ( !rDoc.IsBlockEditable( rRange.aStart.Tab(), rRange.aStart.Col(),rRange.aStart.Row(),
279  rRange.aEnd.Col(),rRange.aEnd.Row() ) )
280  return false;
281 
282  ScRefCellValue aCell(rDoc, rRange.aEnd);
283  ScAddress aPos;
284  return (aCell.meType == CELLTYPE_FORMULA && aCell.mpFormula->GetMatrixOrigin(rDoc, aPos) && aPos == rRange.aStart);
285 }
286 
287 static void lcl_UnLockComment( ScDrawView* pView, const Point& rPos, const ScViewData& rViewData )
288 {
289  if (!pView)
290  return;
291 
292  ScDocument& rDoc = rViewData.GetDocument();
293  ScAddress aCellPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
294  ScPostIt* pNote = rDoc.GetNote( aCellPos );
295  SdrObject* pObj = pNote ? pNote->GetCaption() : nullptr;
296  if( pObj && pObj->GetLogicRect().Contains( rPos ) && ScDrawLayer::IsNoteCaption( pObj ) )
297  {
298  const ScProtectionAttr* pProtAttr = rDoc.GetAttr( aCellPos, ATTR_PROTECTION );
299  bool bProtectAttr = pProtAttr->GetProtection() || pProtAttr->GetHideCell() ;
300  bool bProtectDoc = rDoc.IsTabProtected( aCellPos.Tab() ) || rViewData.GetSfxDocShell()->IsReadOnly() ;
301  // unlock internal layer (if not protected), will be relocked in ScDrawView::MarkListHasChanged()
302  pView->LockInternalLayer( bProtectDoc && bProtectAttr );
303  }
304 }
305 
307  ScDocument& rDoc, SCCOL& rPosX, SCROW nPosY, SCTAB nTab, ScRefCellValue& rCell, OUString& rURL )
308 {
309  bool bFound = false;
310  do
311  {
312  ScAddress aPos(rPosX, nPosY, nTab);
313  rCell.assign(rDoc, aPos);
314  if (rCell.isEmpty())
315  {
316  if ( rPosX <= 0 )
317  return false; // everything empty to the links
318  else
319  --rPosX; // continue search
320  }
321  else
322  {
323  const ScPatternAttr* pPattern = rDoc.GetPattern(aPos);
324  if ( !pPattern->GetItem(ATTR_HYPERLINK).GetValue().isEmpty() )
325  {
326  rURL = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
327  bFound = true;
328  }
329  else if (rCell.meType == CELLTYPE_EDIT)
330  bFound = true;
331  else if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->IsHyperLinkCell())
332  bFound = true;
333  else
334  return false; // other cell
335  }
336  }
337  while ( !bFound );
338 
339  return bFound;
340 }
341 
342 // WB_DIALOGCONTROL needed for UNO-Controls
344 : Window( pParent, WB_CLIPCHILDREN | WB_DIALOGCONTROL ),
345  DropTargetHelper( this ),
346  DragSourceHelper( this ),
347  maVisibleRange(rData.GetDocument()),
348  mrViewData( rData ),
349  eWhich( eWhichPos ),
350  nCursorHideCount( 0 ),
351  nButtonDown( 0 ),
352  nMouseStatus( SC_GM_NONE ),
353  nNestedButtonState( ScNestedButtonState::NONE ),
354  nDPField( 0 ),
355  pDragDPObj( nullptr ),
356  nRFIndex( 0 ),
357  nRFAddX( 0 ),
358  nRFAddY( 0 ),
359  nPagebreakMouse( SC_PD_NONE ),
360  nPagebreakBreak( 0 ),
361  nPagebreakPrev( 0 ),
362  nPageScript( SvtScriptType::NONE ),
363  nDragStartX( -1 ),
364  nDragStartY( -1 ),
365  nDragEndX( -1 ),
366  nDragEndY( -1 ),
367  meDragInsertMode( INS_NONE ),
368  aComboButton( GetOutDev() ),
369  aCurMousePos( 0,0 ),
370  nPaintCount( 0 ),
371  aRFSelectedCorned( NONE ),
372  maShowPageBreaksTimer("ScGridWindow maShowPageBreaksTimer"),
373  bEEMouse( false ),
374  bDPMouse( false ),
375  bRFMouse( false ),
376  bRFSize( false ),
377  bPagebreakDrawn( false ),
378  bDragRect( false ),
379  bIsInPaint( false ),
380  bNeedsRepaint( false ),
381  bAutoMarkVisible( false ),
382  bListValButton( false )
383 {
384  set_id("grid_window");
385  switch(eWhich)
386  {
387  case SC_SPLIT_TOPLEFT:
390  break;
391  case SC_SPLIT_TOPRIGHT:
394  break;
395  case SC_SPLIT_BOTTOMLEFT:
398  break;
402  break;
403  default:
404  OSL_FAIL("GridWindow: wrong position");
405  }
406 
407  SetBackground();
408 
411  SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );
412 
414 
415  GetOutDev()->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() );
416  EnableRTL( false );
417 
418  bInitialPageBreaks = true;
419  maShowPageBreaksTimer.SetInvokeHandler(LINK(this, ScGridWindow, InitiatePageBreaksTimer));
421 }
422 
424 {
425  disposeOnce();
426 }
427 
429 {
431 
433 
434  mpFilterBox.reset();
435  mpNoteMarker.reset();
438  aComboButton.SetOutputDevice(nullptr);
439 
440  if (mpSpellCheckCxt)
441  mpSpellCheckCxt->reset();
442  mpSpellCheckCxt.reset();
443 
445 }
446 
448 {
449  do
450  {
451  // #i84277# when initializing the filter box, a Basic error can deactivate the view
452  if (mpFilterBox && mpFilterBox->IsInInit())
453  break;
454  mpFilterBox.reset();
455  }
456  while (false);
457 
458  if (mpDPFieldPopup)
459  {
460  mpDPFieldPopup->get_widget().close(false);
462  }
463 }
464 
465 IMPL_LINK_NOARG(ScGridWindow, PopupModeEndHdl, weld::Popover&, void)
466 {
467  if (mpFilterBox)
468  {
469  bool bMouseWasCaptured = mpFilterBox->MouseWasCaptured();
470  mpFilterBox->SetCancelled(); // cancel select
471  // restore the mouse capture state of the GridWindow to
472  // what it was at initial popup time
473  if (bMouseWasCaptured)
474  CaptureMouse();
475  }
476  GrabFocus();
477 }
478 
479 IMPL_LINK( ScGridWindow, PopupSpellingHdl, SpellCallbackInfo&, rInfo, void )
480 {
481  if( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
482  mrViewData.GetDispatcher().Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON );
483  else if (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS)
484  mrViewData.GetDispatcher().Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON );
485 }
486 
487 namespace {
488 
489 struct AutoFilterData : public ScCheckListMenuControl::ExtendedData
490 {
491  ScAddress maPos;
492  ScDBData* mpData;
493 };
494 
495 class AutoFilterAction : public ScCheckListMenuControl::Action
496 {
499 public:
500  AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
501  mpWindow(p), meMode(eMode) {}
502  virtual void execute() override
503  {
504  mpWindow->UpdateAutoFilterFromMenu(meMode);
505  }
506 };
507 
508 class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
509 {
510  VclPtr<ScGridWindow> mpWindow;
511  ScAddress maPos;
512 public:
513  AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
514  mpWindow(p), maPos(rPos) {}
515  virtual void execute() override
516  {
517  mpWindow->RefreshAutoFilterButton(maPos);
518  }
519 };
520 
521 class AddItemToEntry
522 {
524  svl::SharedStringPool& mrPool;
525 public:
526  AddItemToEntry(ScQueryEntry::QueryItemsType& rItems, svl::SharedStringPool& rPool) :
527  mrItems(rItems), mrPool(rPool) {}
528  void operator() (const ScCheckListMenuControl::ResultEntry& rEntry)
529  {
530  if (rEntry.bValid)
531  {
532  ScQueryEntry::Item aNew;
533  aNew.maString = mrPool.intern(rEntry.aName);
534  // set the filter type to ByValue, if the filter condition is value
536  aNew.mfVal = rEntry.nValue;
537  mrItems.push_back(aNew);
538  }
539  }
540 };
541 
542 class AddSelectedItemString
543 {
544  std::unordered_set<OUString>& mrSetString;
545  std::unordered_set<double>& mrSetValue;
546 public:
547  explicit AddSelectedItemString(std::unordered_set<OUString>& rString, std::unordered_set<double>& rValue) :
548  mrSetString(rString), mrSetValue(rValue) {}
549 
550  void operator() (const ScQueryEntry::Item& rItem)
551  {
552  if( rItem.meType == ScQueryEntry::QueryType::ByValue )
553  mrSetValue.insert(rItem.mfVal);
554  else
555  mrSetString.insert(rItem.maString.getString());
556  }
557 };
558 
559 void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUString& aevent)
560 {
561  EventDescription aDescription;
562  aDescription.aAction = "LAUNCH";
563  aDescription.aID = "grid_window";
564  aDescription.aParameters = {{aevent, ""},
565  {"ROW", aRow}, {"COL", aCol}};
566  aDescription.aParent = "MainWindow";
567  aDescription.aKeyWord = "ScGridWinUIObject";
568 
569  UITestLogger::getInstance().logEvent(aDescription);
570 }
571 
572 }
573 
575 {
576  SCTAB nTab = mrViewData.GetTabNo();
578  bool bLOKActive = comphelper::LibreOfficeKit::isActive();
579 
581 
582  // Estimate the width (in pixels) of the longest text in the list
583  ScFilterEntries aFilterEntries;
584  rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);
585 
586  vcl::ILibreOfficeKitNotifier* pNotifier = nullptr;
587  if (bLOKActive)
588  pNotifier = SfxViewShell::Current();
589 
590  int nColWidth = ScViewData::ToPixel(rDoc.GetColWidth(nCol, nTab), mrViewData.GetPPTX());
592  aFilterEntries.mbHasDates, nColWidth,
593  nullptr, pNotifier));
595 
596  int nMaxTextWidth = 0;
597  if (aFilterEntries.size() <= 10)
598  {
599  // do pixel calculation for all elements of short lists
600  for (const auto& rEntry : aFilterEntries)
601  {
602  const OUString& aText = rEntry.GetString();
603  nMaxTextWidth = std::max<int>(nMaxTextWidth, rControl.GetTextWidth(aText) + aText.getLength() * 2);
604  }
605  }
606  else
607  {
608  // find the longest string, probably it will be the longest rendered text, too
609  // (performance optimization for long lists)
610  auto itMax = aFilterEntries.begin();
611  for (auto it = itMax; it != aFilterEntries.end(); ++it)
612  {
613  int nTextWidth = it->GetString().getLength();
614  if (nMaxTextWidth < nTextWidth)
615  {
616  nMaxTextWidth = nTextWidth;
617  itMax = it;
618  }
619  }
620  nMaxTextWidth = rControl.GetTextWidth(itMax->GetString()) + nMaxTextWidth * 2;
621  }
622 
623  // window should be at least as wide as the column, or the longest text + checkbox, scrollbar ... (it is estimated with 70 pixel now)
624  // window should be maximum 1024 pixel wide.
625  int nWindowWidth = std::min<int>(1024, nMaxTextWidth + 70);
626  nWindowWidth = rControl.IncreaseWindowWidthToFitText(nWindowWidth);
627  nMaxTextWidth = std::max<int>(nMaxTextWidth, nWindowWidth - 70);
628 
629  rControl.setOKAction(new AutoFilterAction(this, AutoFilterMode::Normal));
630  rControl.setPopupEndAction(
631  new AutoFilterPopupEndAction(this, ScAddress(nCol, nRow, nTab)));
632  std::unique_ptr<AutoFilterData> pData(new AutoFilterData);
633  pData->maPos = ScAddress(nCol, nRow, nTab);
634 
635  Point aPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
636  tools::Long nSizeX = 0;
637  tools::Long nSizeY = 0;
638  mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
639  if (bLOKActive)
640  {
641  // Reverse the zoom factor from aPos and nSize[X|Y]
642  // before letting the autofilter window convert the to twips
643  // with no zoom information.
644  double fZoomX(mrViewData.GetZoomX());
645  double fZoomY(mrViewData.GetZoomY());
646  aPos.setX(aPos.getX() / fZoomX);
647  aPos.setY(aPos.getY() / fZoomY);
648  nSizeX = nSizeX / fZoomX;
649  nSizeY = nSizeY / fZoomY;
650  }
651  tools::Rectangle aCellRect(OutputToScreenPixel(aPos), Size(nSizeX, nSizeY));
652 
653  ScDBData* pDBData = rDoc.GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA);
654  if (!pDBData)
655  return;
656 
657  pData->mpData = pDBData;
658  rControl.setExtendedData(std::move(pData));
659 
660  ScQueryParam aParam;
661  pDBData->GetQueryParam(aParam);
662  std::vector<ScQueryEntry*> aEntries = aParam.FindAllEntriesByField(nCol);
663  std::unordered_set<OUString> aSelectedString;
664  std::unordered_set<double> aSelectedValue;
665  bool bQueryByNonEmpty = aEntries.size() == 1 && aEntries[0]->IsQueryByNonEmpty();
666 
667  if (!bQueryByNonEmpty)
668  {
669  for (ScQueryEntry* pEntry : aEntries)
670  {
671  if (pEntry && pEntry->eOp == SC_EQUAL)
672  {
673  ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
674  std::for_each(rItems.begin(), rItems.end(), AddSelectedItemString(aSelectedString, aSelectedValue));
675  }
676  }
677  }
678 
679  // Populate the check box list.
680  rControl.setMemberSize(aFilterEntries.size());
681  for (auto it = aFilterEntries.begin(); it != aFilterEntries.end(); ++it)
682  {
683  // tdf#140745 show (empty) entry on top of the checkbox list
684  if (it->GetString().isEmpty())
685  {
686  const OUString& aStringVal = it->GetString();
687  const double aDoubleVal = it->GetValue();
688  bool bSelected = true;
689  if (!aSelectedValue.empty() || !aSelectedString.empty())
690  bSelected = aSelectedString.count(aStringVal) > 0;
691  else if (bQueryByNonEmpty)
692  bSelected = false;
693  rControl.addMember(aStringVal, aDoubleVal, bSelected);
694  aFilterEntries.maStrData.erase(it);
695  break;
696  }
697  }
698  for (const auto& rEntry : aFilterEntries)
699  {
700  const OUString& aStringVal = rEntry.GetString();
701  const double aDoubleVal = rEntry.GetValue();
702  const double aRDoubleVal = rEntry.GetRoundedValue();
703  bool bSelected = true;
704 
705  if (!aSelectedValue.empty() || !aSelectedString.empty())
706  {
707  if (aDoubleVal == aRDoubleVal)
708  bSelected = aSelectedValue.count(aDoubleVal) > 0 || aSelectedString.count(aStringVal) > 0;
709  else
710  bSelected = aSelectedValue.count(aDoubleVal) > 0 || aSelectedValue.count(aRDoubleVal) > 0 || aSelectedString.count(aStringVal) > 0;
711  }
712 
713  if ( rEntry.IsDate() )
714  rControl.addDateMember( aStringVal, rEntry.GetValue(), bSelected );
715  else
716  rControl.addMember( aStringVal, aRDoubleVal, bSelected, rEntry.GetStringType() == ScTypedStrData::Value );
717  }
718 
719  // Populate the menu.
720  rControl.addMenuItem(
721  ScResId(STR_MENU_SORT_ASC),
722  new AutoFilterAction(this, AutoFilterMode::SortAscending));
723  rControl.addMenuItem(
724  ScResId(STR_MENU_SORT_DESC),
725  new AutoFilterAction(this, AutoFilterMode::SortDescending));
726  rControl.addSeparator();
727  rControl.addMenuItem(
728  ScResId(SCSTR_TOP10FILTER), new AutoFilterAction(this, AutoFilterMode::Top10));
729  rControl.addMenuItem(
730  ScResId(SCSTR_FILTER_EMPTY), new AutoFilterAction(this, AutoFilterMode::Empty));
731  rControl.addMenuItem(
732  ScResId(SCSTR_FILTER_NOTEMPTY), new AutoFilterAction(this, AutoFilterMode::NonEmpty));
733  rControl.addSeparator();
734  rControl.addMenuItem(
735  ScResId(SCSTR_FILTER_TEXT_COLOR), new AutoFilterAction(this, AutoFilterMode::TextColor));
736  rControl.addMenuItem(
737  ScResId(SCSTR_FILTER_BACKGROUND_COLOR), new AutoFilterAction(this, AutoFilterMode::BackgroundColor));
738  rControl.addSeparator();
739  rControl.addMenuItem(
740  ScResId(SCSTR_STDFILTER), new AutoFilterAction(this, AutoFilterMode::Custom));
741  if (aEntries.size())
742  rControl.addMenuItem(
743  ScResId(SCSTR_CLEAR_FILTER), new AutoFilterAction(this, AutoFilterMode::Clear));
744 
745  rControl.initMembers(nMaxTextWidth + 20); // 20 pixel estimated for the checkbox
746 
748  aConfig.mbAllowEmptySet = false;
750  rControl.setConfig(aConfig);
751  if (IsMouseCaptured())
752  ReleaseMouse();
753  rControl.launch(aCellRect);
754 
755  // remember filter rules before modification
757 
758  collectUIInformation(OUString::number(nRow), OUString::number(nCol),"AUTOFILTER");
759 }
760 
762 {
763  if (mpFilterButton)
764  {
765  bool bFilterActive = IsAutoFilterActive(rPos.Col(), rPos.Row(), rPos.Tab());
766  mpFilterButton->setHasHiddenMember(bFilterActive);
767  mpFilterButton->setPopupPressed(false);
768  mpFilterButton->draw();
769  }
770 }
771 
773 {
775 
776  // Terminate autofilter popup now when there is no further user input needed
777  bool bColorMode = eMode == AutoFilterMode::TextColor || eMode == AutoFilterMode::BackgroundColor;
778  if (!bColorMode)
779  rControl.terminateAllPopupMenus();
780 
781  const AutoFilterData* pData =
782  static_cast<const AutoFilterData*>(rControl.getExtendedData());
783 
784  if (!pData)
785  return;
786 
787  const ScAddress& rPos = pData->maPos;
788  ScDBData* pDBData = pData->mpData;
789  if (!pDBData)
790  return;
791 
794  switch (eMode)
795  {
798  {
799  SCCOL nCol = rPos.Col();
800  ScSortParam aSortParam;
801  pDBData->GetSortParam(aSortParam);
802  if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
803  // out of bound
804  return;
805 
806  bool bHasHeader = pDBData->HasHeader();
807 
808  aSortParam.bHasHeader = bHasHeader;
809  aSortParam.bByRow = true;
810  aSortParam.bCaseSens = false;
811  aSortParam.bNaturalSort = false;
812  aSortParam.aDataAreaExtras.mbCellNotes = false;
813  aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
814  aSortParam.aDataAreaExtras.mbCellFormats = true;
815  aSortParam.bInplace = true;
816  aSortParam.maKeyState[0].bDoSort = true;
817  aSortParam.maKeyState[0].nField = nCol;
818  aSortParam.maKeyState[0].bAscending = (eMode == AutoFilterMode::SortAscending);
819 
820  for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
821  aSortParam.maKeyState[i].bDoSort = false;
822 
823  mrViewData.GetViewShell()->UISort(aSortParam);
824  return;
825  }
827  {
828  ScRange aRange;
829  pDBData->GetArea(aRange);
830  mrViewData.GetView()->MarkRange(aRange);
831  mrViewData.GetView()->SetCursor(rPos.Col(), rPos.Row());
832  mrViewData.GetDispatcher().Execute(SID_FILTER, SfxCallMode::SLOT | SfxCallMode::RECORD);
833  return;
834  }
835  default:
836  ;
837  }
838 
839  ScQueryParam aParam;
840  pDBData->GetQueryParam(aParam);
841 
842  if (eMode == AutoFilterMode::Normal)
843  {
844  // Do not recreate autofilter rules if there are no changes from the user
846  rControl.getResult(aResult);
847 
848  if (aResult == aSaveAutoFilterResult)
849  {
850  SAL_INFO("sc.ui", "Apply autofilter to data when entries are the same");
851 
852  if (!rControl.isAllSelected())
853  {
854  // Apply autofilter to data
855  ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
856  pEntry->bDoQuery = true;
857  pEntry->nField = rPos.Col();
858  pEntry->eConnect = SC_AND;
859  pEntry->eOp = SC_EQUAL;
860  mrViewData.GetView()->Query(aParam, nullptr, true);
861  }
862 
863  return;
864  }
865  }
866 
867  // Remove old entries in auto-filter rules
868  if (!bColorMode)
869  {
870  aParam.RemoveAllEntriesByField(rPos.Col());
871 
872  // tdf#46184 reset filter options to default values
874  aParam.bCaseSens = false;
875  aParam.bDuplicate = true;
876  aParam.bInplace = true;
877  }
878 
879  if (eMode != AutoFilterMode::Clear
880  && !(eMode == AutoFilterMode::Normal && rControl.isAllSelected()))
881  {
882  // Try to use the existing entry for the column (if one exists).
883  ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
884 
885  if (!pEntry)
886  // Something went terribly wrong!
887  return;
888 
889  if (ScTabViewShell::isAnyEditViewInRange(mrViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
890  return;
891 
892  pEntry->bDoQuery = true;
893  pEntry->nField = rPos.Col();
894  pEntry->eConnect = SC_AND;
895 
896  switch (eMode)
897  {
899  {
900  pEntry->eOp = SC_EQUAL;
901 
903  rControl.getResult(aResult);
904 
905  ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
906  rItems.clear();
907  std::for_each(aResult.begin(), aResult.end(), AddItemToEntry(rItems, rPool));
908  }
909  break;
911  pEntry->eOp = SC_TOPVAL;
913  pEntry->GetQueryItem().maString = rPool.intern("10");
914  break;
916  pEntry->SetQueryByEmpty();
917  break;
919  pEntry->SetQueryByNonEmpty();
920  break;
923  {
924  ScFilterEntries aFilterEntries;
925  rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
926 
928  std::set<Color> aColors = eMode == AutoFilterMode::TextColor
929  ? aFilterEntries.getTextColors()
930  : aFilterEntries.getBackgroundColors();
931 
932  sal_Int32 i = 1;
933  sal_Int32 nActive = -1;
934  for (auto& rColor : aColors)
935  {
936  if (rColor == COL_AUTO)
937  {
938  OUString sText = eMode == AutoFilterMode::TextColor
939  ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
940  : ScResId(SCSTR_FILTER_NO_FILL);
941  pColorMenu->InsertItem(i, sText, MenuItemBits::CHECKABLE);
942  }
943  else
944  {
945  pColorMenu->InsertItem(i, OUString(), MenuItemBits::CHECKABLE);
946  pColorMenu->SetItemColor(i, rColor);
947  }
948  auto aItem = pEntry->GetQueryItem();
949  if (aItem.maColor == rColor
950  && ((eMode == AutoFilterMode::TextColor
951  && aItem.meType == ScQueryEntry::ByTextColor)
953  && aItem.meType == ScQueryEntry::ByBackgroundColor)))
954  {
955  nActive = i;
956  pColorMenu->CheckItem(i, true);
957  }
958  i++;
959  }
961  pos.Move(150, 0);
962  sal_uInt16 nSelected = pColorMenu->Execute(this, pos);
963  pColorMenu.disposeAndClear();
964  rControl.terminateAllPopupMenus();
965 
966  if (nSelected == 0)
967  return;
968 
969  // Disable color filter when active color was selected
970  if (nSelected == nActive)
971  {
972  aParam.RemoveAllEntriesByField(rPos.Col());
973  pEntry = nullptr; // invalidated by RemoveAllEntriesByField call
974 
975  // tdf#46184 reset filter options to default values
977  aParam.bCaseSens = false;
978  aParam.bDuplicate = true;
979  aParam.bInplace = true;
980  }
981  else
982  {
983  // Get selected color from set
984  std::set<Color>::iterator it = aColors.begin();
985  std::advance(it, nSelected - 1);
986  Color selectedColor = *it;
987 
988  if (eMode == AutoFilterMode::TextColor)
989  pEntry->SetQueryByTextColor(selectedColor);
990  else
991  pEntry->SetQueryByBackgroundColor(selectedColor);
992  }
993  }
994 
995  break;
996  default:
997  // We don't know how to handle this!
998  return;
999  }
1000  }
1001 
1002  mrViewData.GetView()->Query(aParam, nullptr, true);
1003  pDBData->SetQueryParam(aParam);
1004 }
1005 
1006 namespace {
1007 
1008 void getCellGeometry(Point& rScrPos, Size& rScrSize, const ScViewData& rViewData, SCCOL nCol, SCROW nRow, ScSplitPos eWhich)
1009 {
1010  // Get the screen position of the cell.
1011  rScrPos = rViewData.GetScrPos(nCol, nRow, eWhich);
1012 
1013  // Get the screen size of the cell.
1014  tools::Long nSizeX, nSizeY;
1015  rViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
1016  rScrSize = Size(nSizeX-1, nSizeY-1);
1017 }
1018 
1019 }
1020 
1022 {
1023  if (nCol == 0)
1024  // We assume that the page field button is located in cell to the immediate left.
1025  return;
1026 
1027  SCTAB nTab = mrViewData.GetTabNo();
1028  ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
1029  if (!pDPObj)
1030  return;
1031 
1032  Point aScrPos;
1033  Size aScrSize;
1034  getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
1035  DPLaunchFieldPopupMenu(OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol-1, nRow, nTab), pDPObj);
1036 }
1037 
1039 {
1040  SCTAB nTab = mrViewData.GetTabNo();
1041  ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
1042  if (!pDPObj)
1043  return;
1044 
1045  Point aScrPos;
1046  Size aScrSize;
1047  getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
1048  DPLaunchFieldPopupMenu(OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol, nRow, nTab), pDPObj);
1049 }
1050 
1051 void ScGridWindow::ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL)
1052 {
1053  auto nSizeX = rCellRect.GetWidth();
1054 
1055  // minimum width in pixel
1057  {
1058  const tools::Long nMinLOKWinWidth = o3tl::convert(STD_COL_WIDTH * 13 / 10, o3tl::Length::twip, o3tl::Length::px);
1059  if (nSizeX < nMinLOKWinWidth)
1060  nSizeX = nMinLOKWinWidth;
1061  }
1062 
1063  weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1064  int nEntryCount = rFilterBox.n_children();
1065  if (nEntryCount > SC_FILTERLISTBOX_LINES)
1066  nEntryCount = SC_FILTERLISTBOX_LINES;
1067  auto nHeight = rFilterBox.get_height_rows(nEntryCount);
1068  rFilterBox.set_size_request(-1, nHeight);
1069  Size aSize(rFilterBox.get_preferred_size());
1070  auto nMaxToExpandTo = std::min(nSizeX, static_cast<decltype(nSizeX)>(300)); // do not over do it (Pixel)
1071  if (aSize.Width() < nMaxToExpandTo)
1072  aSize.setWidth(nMaxToExpandTo);
1073 
1074  aSize.AdjustWidth(4); // add a little margin
1075  nSizeX += 4;
1076  aSize.AdjustHeight(4);
1077 
1078  tools::Rectangle aCellRect(rCellRect);
1079  aCellRect.AdjustLeft(-2); // offset the little margin above
1080 
1081  if (!bLayoutRTL && aSize.Width() > nSizeX)
1082  {
1083  // move popup position
1084  tools::Long nDiff = aSize.Width() - nSizeX;
1085  tools::Long nNewX = aCellRect.Left() - nDiff;
1086  if ( nNewX < 0 )
1087  nNewX = 0;
1088  aCellRect.SetLeft( nNewX );
1089  }
1090 
1091  rFilterBox.set_size_request(aSize.Width(), aSize.Height());
1092 
1093  if (IsMouseCaptured())
1094  ReleaseMouse();
1095  mpFilterBox->popup_at_rect(pParent, aCellRect);
1096 }
1097 
1098 void ScGridWindow::DoScenarioMenu( const ScRange& rScenRange )
1099 {
1100  bool bMenuAtTop = true;
1101 
1102  ScDocument& rDoc = mrViewData.GetDocument();
1103  mpFilterBox.reset();
1104 
1105  SCCOL nCol = rScenRange.aEnd.Col(); // Cell is below the Buttons
1106  SCROW nRow = rScenRange.aStart.Row();
1107  if (nRow == 0)
1108  {
1109  nRow = rScenRange.aEnd.Row() + 1; // Range at very the top -> Button below
1110  if (nRow>rDoc.MaxRow()) nRow = rDoc.MaxRow();
1111  bMenuAtTop = false;
1112  }
1113 
1114  SCTAB nTab = mrViewData.GetTabNo();
1115  bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1116 
1117  tools::Long nSizeX = 0;
1118  tools::Long nSizeY = 0;
1119  mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
1120  // The button height should not use the merged cell height, should still use single row height
1121  nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
1122  Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
1123  if ( bLayoutRTL )
1124  aPos.AdjustX( -nSizeX );
1125  tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
1126  aCellRect.AdjustTop( -nSizeY );
1127  aCellRect.AdjustBottom( -(nSizeY - 1) );
1128  if (!bMenuAtTop)
1129  {
1130  Size aButSize = mrViewData.GetScenButSize();
1131  aCellRect.AdjustBottom(aButSize.Height());
1132  }
1133 
1134  // Place the ListBox directly below the black line of the cell grid
1135  // (It looks odd if the line gets hidden...)
1136 
1137  weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
1138  mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::Scenario);
1139  mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
1140  weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1141  rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
1142 
1143  // Listbox fill
1144  rFilterBox.freeze();
1145  OUString aCurrent;
1146  OUString aTabName;
1147  SCTAB nTabCount = rDoc.GetTableCount();
1148  SCTAB nEntryCount = 0;
1149  for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
1150  {
1151  if (rDoc.HasScenarioRange( i, rScenRange ))
1152  if (rDoc.GetName( i, aTabName ))
1153  {
1154  rFilterBox.append_text(aTabName);
1155  if (rDoc.IsActiveScenario(i))
1156  aCurrent = aTabName;
1157  ++nEntryCount;
1158  }
1159  }
1160  rFilterBox.thaw();
1161 
1162  ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
1163 
1164  rFilterBox.grab_focus();
1165 
1166  sal_Int32 nPos = -1;
1167  if (!aCurrent.isEmpty())
1168  {
1169  nPos = rFilterBox.find_text(aCurrent);
1170  }
1171  if (nPos == -1 && rFilterBox.n_children() > 0 )
1172  {
1173  nPos = 0;
1174  }
1175  if (nPos != -1)
1176  {
1177  rFilterBox.set_cursor(nPos);
1178  rFilterBox.select(nPos);
1179  }
1180  mpFilterBox->EndInit();
1181 }
1182 
1184 {
1185  mpFilterBox.reset();
1186 
1187  ScDocument& rDoc = mrViewData.GetDocument();
1188  SCTAB nTab = mrViewData.GetTabNo();
1189  bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1190 
1191  tools::Long nSizeX = 0;
1192  tools::Long nSizeY = 0;
1193  mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
1194  Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
1195  bool bLOKActive = comphelper::LibreOfficeKit::isActive();
1196 
1197  if (bLOKActive)
1198  {
1199  // aPos is now view-zoom adjusted and in pixels an more importantly this is pixel aligned to the view-zoom,
1200  // but once we use this to set the position of the floating window, it has no information of view-zoom level
1201  // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, MapMode(MapUnit::MapTwip)) employed in
1202  // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position which will again get zoom scaled in the
1203  // client (effective double scaling) causing wrong positioning/size.
1204  double fZoomX(mrViewData.GetZoomX());
1205  double fZoomY(mrViewData.GetZoomY());
1206  aPos.setX(aPos.getX() / fZoomX);
1207  aPos.setY(aPos.getY() / fZoomY);
1208  nSizeX = nSizeX / fZoomX;
1209  nSizeY = nSizeY / fZoomY;
1210  }
1211 
1212  if ( bLayoutRTL )
1213  aPos.AdjustX( -nSizeX );
1214  tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
1215 
1216  weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
1217  mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::DataSelect);
1218  mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
1219  weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1220  rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
1221 
1222  // SetSize later
1223 
1224  bool bEmpty = false;
1225  std::vector<ScTypedStrData> aStrings; // case sensitive
1226  // Fill List
1227  rDoc.GetDataEntries(nCol, nRow, nTab, aStrings, true /* bValidation */);
1228  if (aStrings.empty())
1229  bEmpty = true;
1230 
1231  if (!bEmpty)
1232  {
1233  rFilterBox.freeze();
1234 
1235  // Fill Listbox
1236  bool bWait = aStrings.size() > 100;
1237 
1238  if (bWait)
1239  EnterWait();
1240 
1241  for (const auto& rString : aStrings)
1242  rFilterBox.append_text(rString.GetString());
1243 
1244  if (bWait)
1245  LeaveWait();
1246 
1247  rFilterBox.thaw();
1248 
1249  ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
1250  }
1251 
1252  sal_Int32 nSelPos = -1;
1253 
1254  sal_uLong nIndex = rDoc.GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
1255  if ( nIndex )
1256  {
1257  const ScValidationData* pData = rDoc.GetValidationEntry( nIndex );
1258  if (pData)
1259  {
1260  std::unique_ptr<ScTypedStrData> pNew;
1261  OUString aDocStr = rDoc.GetString(nCol, nRow, nTab);
1262  if ( rDoc.HasValueData( nCol, nRow, nTab ) )
1263  {
1264  double fVal = rDoc.GetValue(ScAddress(nCol, nRow, nTab));
1265  pNew.reset(new ScTypedStrData(aDocStr, fVal, ScTypedStrData::Value));
1266  }
1267  else
1268  pNew.reset(new ScTypedStrData(aDocStr, 0.0, ScTypedStrData::Standard));
1269 
1270  if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
1271  {
1272  auto it = std::lower_bound(aStrings.begin(), aStrings.end(), *pNew, ScTypedStrData::LessCaseSensitive());
1273  if (it != aStrings.end() && ScTypedStrData::EqualCaseSensitive()(*it, *pNew))
1274  nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
1275  }
1276  else
1277  {
1278  auto it = std::find_if(aStrings.begin(), aStrings.end(), FindTypedStrData(*pNew, true));
1279  if (it != aStrings.end())
1280  nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
1281  }
1282  }
1283  }
1284 
1285  // Do not show an empty selection List:
1286 
1287  if ( bEmpty )
1288  {
1289  mpFilterBox.reset();
1290  }
1291  else
1292  {
1293  rFilterBox.grab_focus();
1294 
1295  // Select only after GrabFocus, so that the focus rectangle gets correct
1296  if (nSelPos != -1)
1297  {
1298  rFilterBox.set_cursor(nSelPos);
1299  rFilterBox.select(nSelPos);
1300  }
1301  else
1302  rFilterBox.unselect_all();
1303 
1304  mpFilterBox->EndInit();
1305  }
1306  collectUIInformation(OUString::number(nRow), OUString::number(nCol),"SELECTMENU");
1307 }
1308 
1310 {
1311  weld::TreeView& rFilterBox = mpFilterBox->get_widget();
1312  OUString aString = rFilterBox.get_text(static_cast<sal_Int32>(nSel));
1313 
1314  SCCOL nCol = mpFilterBox->GetCol();
1315  SCROW nRow = mpFilterBox->GetRow();
1316  switch (mpFilterBox->GetMode())
1317  {
1319  ExecDataSelect(nCol, nRow, aString);
1320  break;
1322  mrViewData.GetView()->UseScenario(aString);
1323  break;
1324  }
1325 
1326  if (mpFilterBox)
1327  mpFilterBox->popdown();
1328 
1329  GrabFocus(); // Otherwise the focus would be wrong on OS/2
1330 }
1331 
1332 void ScGridWindow::ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr )
1333 {
1334  ScModule* pScMod = SC_MOD();
1335  ScInputHandler* pViewHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
1336  if (pViewHdl && mrViewData.HasEditView(mrViewData.GetActivePart()))
1337  pViewHdl->CancelHandler();
1338 
1339  SCTAB nTab = mrViewData.GetTabNo();
1340  ScViewFunc* pView = mrViewData.GetView();
1341  pView->EnterData( nCol, nRow, nTab, rStr );
1342 
1343  // #i52307# CellContentChanged is not in EnterData so it isn't called twice
1344  // if the cursor is moved afterwards.
1345  pView->CellContentChanged();
1346 }
1347 
1349 {
1350  if (nButtonDown)
1351  {
1352  rDestWin.nButtonDown = nButtonDown;
1353  rDestWin.nMouseStatus = nMouseStatus;
1354  }
1355 
1356  if (bRFMouse)
1357  {
1358  rDestWin.bRFMouse = bRFMouse;
1359  rDestWin.bRFSize = bRFSize;
1360  rDestWin.nRFIndex = nRFIndex;
1361  rDestWin.nRFAddX = nRFAddX;
1362  rDestWin.nRFAddY = nRFAddY;
1363  bRFMouse = false;
1364  }
1365 
1366  if (nPagebreakMouse)
1367  {
1368  rDestWin.nPagebreakMouse = nPagebreakMouse;
1369  rDestWin.nPagebreakBreak = nPagebreakBreak;
1370  rDestWin.nPagebreakPrev = nPagebreakPrev;
1372  rDestWin.aPagebreakDrag = aPagebreakDrag;
1374  }
1375 }
1376 
1377 bool ScGridWindow::TestMouse( const MouseEvent& rMEvt, bool bAction )
1378 {
1379  // MouseEvent buttons must only be checked if bAction==TRUE
1380  // to allow changing the mouse pointer in MouseMove,
1381  // but not start AutoFill with right button (#74229#).
1382  // with bAction==sal_True, SetFillMode / SetDragMode is called
1383 
1384  if ( bAction && !rMEvt.IsLeft() )
1385  return false;
1386 
1387  bool bNewPointer = false;
1388 
1390  bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
1391 
1392  if ( mrViewData.IsActive() && !bOleActive )
1393  {
1394  ScDocument& rDoc = mrViewData.GetDocument();
1395  SCTAB nTab = mrViewData.GetTabNo();
1396  bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
1397 
1398  // Auto-Fill
1399 
1400  ScRange aMarkRange;
1401  if (mrViewData.GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE)
1402  {
1403  if (aMarkRange.aStart.Tab() == mrViewData.GetTabNo() && mpAutoFillRect)
1404  {
1405  Point aMousePos = rMEvt.GetPosPixel();
1406  if (mpAutoFillRect->Contains(aMousePos))
1407  {
1408  SetPointer( PointerStyle::Cross );
1409  if (bAction)
1410  {
1411  SCCOL nX = aMarkRange.aEnd.Col();
1412  SCROW nY = aMarkRange.aEnd.Row();
1413 
1414  if ( lcl_IsEditableMatrix( mrViewData.GetDocument(), aMarkRange ) )
1416  aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY, ScFillMode::MATRIX );
1417  else
1419  aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY );
1420 
1421  // The simple selection must also be recognized when dragging,
1422  // where the Marking flag is set and MarkToSimple won't work anymore.
1424  }
1425  bNewPointer = true;
1426  }
1427  }
1428  }
1429 
1430  // Embedded rectangle
1431 
1432  if (rDoc.IsEmbedded())
1433  {
1434  ScRange aRange;
1435  rDoc.GetEmbedded( aRange );
1436  if ( mrViewData.GetTabNo() == aRange.aStart.Tab() )
1437  {
1438  Point aStartPos = mrViewData.GetScrPos( aRange.aStart.Col(), aRange.aStart.Row(), eWhich );
1439  Point aEndPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich );
1440  Point aMousePos = rMEvt.GetPosPixel();
1441  if ( bLayoutRTL )
1442  {
1443  aStartPos.AdjustX(2 );
1444  aEndPos.AdjustX(2 );
1445  }
1446  bool bTop = ( aMousePos.X() >= aStartPos.X()-3 && aMousePos.X() <= aStartPos.X()+1 &&
1447  aMousePos.Y() >= aStartPos.Y()-3 && aMousePos.Y() <= aStartPos.Y()+1 );
1448  bool bBottom = ( aMousePos.X() >= aEndPos.X()-3 && aMousePos.X() <= aEndPos.X()+1 &&
1449  aMousePos.Y() >= aEndPos.Y()-3 && aMousePos.Y() <= aEndPos.Y()+1 );
1450  if ( bTop || bBottom )
1451  {
1452  SetPointer( PointerStyle::Cross );
1453  if (bAction)
1454  {
1457  aRange.aStart.Col(), aRange.aStart.Row(),
1458  aRange.aEnd.Col(), aRange.aEnd.Row(), nMode );
1459  }
1460  bNewPointer = true;
1461  }
1462  }
1463  }
1464  }
1465 
1466  if (!bNewPointer && bAction)
1467  {
1469  }
1470 
1471  return bNewPointer;
1472 }
1473 
1475 {
1476  MouseButtonDown(rMouseEvent);
1477 }
1478 
1480 {
1481  MouseButtonUp(rMouseEvent);
1482 }
1483 
1485 {
1486  MouseMove(rMouseEvent);
1487 }
1488 
1490 {
1491  if (SfxLokHelper::getDeviceFormFactor() == LOKDeviceFormFactor::MOBILE)
1492  {
1493  ScViewFunc* pView = mrViewData.GetView();
1494  ScTabViewShell* pViewShell = mrViewData.GetViewShell();
1495  bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
1496 
1497  Point aPos(rMEvt.GetPosPixel());
1498  SCCOL nPosX;
1499  SCROW nPosY;
1500  mrViewData.GetPosFromPixel(aPos.X(), aPos.Y(), eWhich, nPosX, nPosY);
1501 
1502  if (bRefMode && pView->GetFunctionSet().CheckRefBounds(nPosX, nPosY))
1503  return;
1504  }
1505 
1507 
1508  MouseEventState aState;
1509  HandleMouseButtonDown(rMEvt, aState);
1510  if (aState.mbActivatePart)
1511  mrViewData.GetView()->ActivatePart(eWhich);
1512 
1514  {
1515  // #i41690# If an object is deactivated from MouseButtonDown, it might reschedule,
1516  // so MouseButtonUp comes before the MouseButtonDown call is finished. In this case,
1517  // simulate another MouseButtonUp call, so the selection state is consistent.
1518 
1519  nButtonDown = rMEvt.GetButtons();
1520  FakeButtonUp();
1521 
1522  if ( IsTracking() )
1523  EndTracking(); // normally done in VCL as part of MouseButtonUp handling
1524  }
1526 }
1527 
1528 bool ScGridWindow::IsCellCoveredByText(SCCOL nPosX, SCROW nPosY, SCTAB nTab, SCCOL &rTextStartPosX)
1529 {
1530  ScDocument& rDoc = mrViewData.GetDocument();
1531 
1532  // find the first non-empty cell (this, or to the left)
1533  SCCOL nNonEmptyX = nPosX;
1534  for (; nNonEmptyX >= 0; --nNonEmptyX)
1535  {
1536  ScRefCellValue aCell(rDoc, ScAddress(nNonEmptyX, nPosY, nTab));
1537  if (!aCell.isEmpty())
1538  break;
1539  }
1540 
1541  // the initial cell already contains text
1542  if (nNonEmptyX == nPosX)
1543  {
1544  rTextStartPosX = nNonEmptyX;
1545  return true;
1546  }
1547 
1548  // to the left, there is no cell that would contain (potentially
1549  // overrunning) text
1550  if (nNonEmptyX < 0 || rDoc.HasAttrib(nNonEmptyX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped))
1551  return false;
1552 
1553  double nPPTX = mrViewData.GetPPTX();
1554  double nPPTY = mrViewData.GetPPTY();
1555 
1556  ScTableInfo aTabInfo;
1557  rDoc.FillInfo(aTabInfo, 0, nPosY, nPosX, nPosY, nTab, nPPTX, nPPTY, false, false);
1558 
1559  Fraction aZoomX = mrViewData.GetZoomX();
1560  Fraction aZoomY = mrViewData.GetZoomY();
1561  ScOutputData aOutputData(GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
1562  0, 0, 0, nPosY, nPosX, nPosY, nPPTX, nPPTY,
1563  &aZoomX, &aZoomY);
1564 
1565  MapMode aCurrentMapMode(GetMapMode());
1566  SetMapMode(MapMode(MapUnit::MapPixel));
1567 
1568  // obtain the bounding box of the text in first non-empty cell
1569  // to the left
1570  tools::Rectangle aRect(aOutputData.LayoutStrings(false, false, ScAddress(nNonEmptyX, nPosY, nTab)));
1571 
1572  SetMapMode(aCurrentMapMode);
1573 
1574  // the text does not overrun from the cell
1575  if (aRect.IsEmpty())
1576  return false;
1577 
1578  SCCOL nTextEndX;
1579  SCROW nTextEndY;
1580 
1581  // test the rightmost position of the text bounding box
1582  tools::Long nMiddle = (aRect.Top() + aRect.Bottom()) / 2;
1583  mrViewData.GetPosFromPixel(aRect.Right(), nMiddle, eWhich, nTextEndX, nTextEndY);
1584  if (nTextEndX >= nPosX)
1585  {
1586  rTextStartPosX = nNonEmptyX;
1587  return true;
1588  }
1589 
1590  return false;
1591 }
1592 
1594 {
1595  // We have to check if a context menu is shown and we have an UI
1596  // active inplace client. In that case we have to ignore the event.
1597  // Otherwise we would crash (context menu has been
1598  // opened by inplace client and we would deactivate the inplace client,
1599  // the context menu is closed by VCL asynchronously which in the end
1600  // would work on deleted objects or the context menu has no parent anymore)
1601  SfxViewShell* pViewSh = mrViewData.GetViewShell();
1602  SfxInPlaceClient* pClient = pViewSh->GetIPClient();
1603  if ( pClient &&
1604  pClient->IsObjectInPlaceActive() &&
1606  return;
1607 
1608  aCurMousePos = rMEvt.GetPosPixel();
1609 
1610  // Filter popup is ended with its own mouse click, not when clicking into the Grid Window,
1611  // so the following query is no longer necessary:
1612  ClickExtern(); // deletes FilterBox when available
1613 
1614  HideNoteMarker();
1615 
1616  bEEMouse = false;
1617 
1618  ScModule* pScMod = SC_MOD();
1619  if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
1620  return;
1621 
1622  pScActiveViewShell = mrViewData.GetViewShell(); // if left is clicked
1623  nScClickMouseModifier = rMEvt.GetModifier(); // to always catch a control click
1624 
1625  bool bDetective = mrViewData.GetViewShell()->IsAuditShell();
1626  bool bRefMode = mrViewData.IsRefMode(); // Start reference
1627  bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
1628  bool bEditMode = mrViewData.HasEditView(eWhich); // also in Mode==SC_INPUT_TYPE
1629  bool bDouble = (rMEvt.GetClicks() == 2);
1630  ScDocument& rDoc = mrViewData.GetDocument();
1631  bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
1632 
1633  // DeactivateIP does only happen when MarkListHasChanged
1634 
1635  // An error message can show up during GrabFocus call
1636  // (for instance when renaming tables per sheet title)
1637 
1638  if ( !nButtonDown || !bDouble ) // single (first) click is always valid
1639  nButtonDown = rMEvt.GetButtons(); // set nButtonDown first, so StopMarking works
1640 
1641  // special handling of empty cells with tiled rendering
1642  if (bIsTiledRendering)
1643  {
1644  Point aPos(rMEvt.GetPosPixel());
1645  SCCOL nPosX, nNonEmptyX(0);
1646  SCROW nPosY;
1647  SCTAB nTab = mrViewData.GetTabNo();
1648  mrViewData.GetPosFromPixel(aPos.X(), aPos.Y(), eWhich, nPosX, nPosY);
1649 
1650  ScRefCellValue aCell(rDoc, ScAddress(nPosX, nPosY, nTab));
1651  bool bIsEmpty = aCell.isEmpty();
1652  bool bIsCoveredByText = bIsEmpty && IsCellCoveredByText(nPosX, nPosY, nTab, nNonEmptyX);
1653 
1654  if (bIsCoveredByText)
1655  {
1656  // if there's any text flowing to this cell, activate the
1657  // editengine, so that the text actually gets the events
1658  if (bDouble)
1659  {
1660  ScViewFunc* pView = mrViewData.GetView();
1661 
1662  pView->SetCursor(nNonEmptyX, nPosY);
1663  SC_MOD()->SetInputMode(SC_INPUT_TABLE);
1664 
1665  bEditMode = mrViewData.HasEditView(eWhich);
1666  assert(bEditMode);
1667 
1668  // synthesize the 1st click
1669  EditView* pEditView = mrViewData.GetEditView(eWhich);
1670  MouseEvent aEditEvt(rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0);
1671  pEditView->MouseButtonDown(aEditEvt);
1672  pEditView->MouseButtonUp(aEditEvt);
1673  }
1674  }
1675  else if (bIsEmpty && bEditMode && bDouble)
1676  {
1677  // double-click in an empty cell: the entire cell is selected
1678  SetCellSelectionPixel(LOK_SETTEXTSELECTION_START, aPos.X(), aPos.Y());
1679  SetCellSelectionPixel(LOK_SETTEXTSELECTION_END, aPos.X(), aPos.Y());
1680  return;
1681  }
1682  }
1683 
1684  if ( ( bEditMode && mrViewData.GetActivePart() == eWhich ) || !bFormulaMode )
1685  GrabFocus();
1686 
1687  // #i31846# need to cancel a double click if the first click has set the "ignore" state,
1688  // but a single (first) click is always valid
1689  if ( nMouseStatus == SC_GM_IGNORE && bDouble )
1690  {
1691  nButtonDown = 0;
1693  return;
1694  }
1695 
1696  if ( bDetective ) // Detectiv fill mode
1697  {
1698  if ( rMEvt.IsLeft() && !rMEvt.GetModifier() )
1699  {
1700  Point aPos = rMEvt.GetPosPixel();
1701  SCCOL nPosX;
1702  SCROW nPosY;
1703  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1704 
1705  SfxInt16Item aPosXItem( SID_RANGE_COL, nPosX );
1706  SfxInt32Item aPosYItem( SID_RANGE_ROW, nPosY );
1707  mrViewData.GetDispatcher().ExecuteList(SID_FILL_SELECT,
1708  SfxCallMode::SLOT | SfxCallMode::RECORD,
1709  { &aPosXItem, &aPosYItem });
1710 
1711  }
1712  nButtonDown = 0;
1714  return;
1715  }
1716 
1717  if (!bDouble)
1719 
1720  rState.mbActivatePart = !bFormulaMode; // Don't activate when in formula mode.
1721 
1722  if (bFormulaMode)
1723  {
1725  pSelEng->SetWindow(this);
1726  pSelEng->SetWhich(eWhich);
1728  }
1729 
1730  if (bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()))
1731  {
1732  Point aPos = rMEvt.GetPosPixel();
1733  SCCOL nPosX;
1734  SCROW nPosY;
1735  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1736 
1737  EditView* pEditView;
1738  SCCOL nEditCol;
1739  SCROW nEditRow;
1740  mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
1741  SCCOL nEndCol = mrViewData.GetEditEndCol();
1742  SCROW nEndRow = mrViewData.GetEditEndRow();
1743 
1744  if ( nPosX >= nEditCol && nPosX <= nEndCol &&
1745  nPosY >= nEditRow && nPosY <= nEndRow )
1746  {
1747  // when clicking in the table EditView, always reset the focus
1748  if (bFormulaMode) // otherwise this has already happen above
1749  GrabFocus();
1750 
1751  pScMod->SetInputMode( SC_INPUT_TABLE );
1752  bEEMouse = true;
1753  pEditView->MouseButtonDown( rMEvt );
1754  return;
1755  }
1756  }
1757 
1758  if (pScMod->GetIsWaterCan())
1759  {
1761  if ( rMEvt.GetModifier() + rMEvt.GetButtons() == MOUSE_RIGHT )
1762  {
1764  return;
1765  }
1766  }
1767 
1768  // Order that matches the displayed Cursor:
1769  // RangeFinder, AutoFill, PageBreak, Drawing
1770 
1771  RfCorner rCorner = NONE;
1772  bool bFound = HitRangeFinder(rMEvt.GetPosPixel(), rCorner, &nRFIndex, &nRFAddX, &nRFAddY);
1773  bRFSize = (rCorner != NONE);
1774  aRFSelectedCorned = rCorner;
1775 
1776  if (bFound)
1777  {
1778  bRFMouse = true; // the other variables are initialized above
1779 
1780  rState.mbActivatePart = true; // always activate ?
1781  StartTracking();
1782  return;
1783  }
1784 
1785  bool bCrossPointer = TestMouse( rMEvt, true );
1786  if ( bCrossPointer )
1787  {
1788  if ( bDouble )
1790  else
1791  pScMod->InputEnterHandler(); // Autofill etc.
1792  }
1793 
1794  if ( !bCrossPointer )
1795  {
1798  if (nPagebreakMouse)
1799  {
1800  bPagebreakDrawn = false;
1801  StartTracking();
1802  PagebreakMove( rMEvt, false );
1803  return;
1804  }
1805  }
1806 
1807  // in the tiled rendering case, single clicks into drawing objects take
1808  // precedence over bEditMode
1809  if (((!bFormulaMode && !bEditMode) || bIsTiledRendering) && rMEvt.IsLeft())
1810  {
1811  if ( !bCrossPointer && DrawMouseButtonDown(rMEvt) )
1812  {
1813  return;
1814  }
1815 
1816  mrViewData.GetViewShell()->SetDrawShell( false ); // no Draw-object selected
1817 
1818  // TestMouse has already happened above
1819  }
1820 
1821  Point aPos = rMEvt.GetPosPixel();
1822  SCCOL nPosX;
1823  SCROW nPosY;
1824  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
1825  SCTAB nTab = mrViewData.GetTabNo();
1826 
1827  // FIXME: this is to limit the number of rows handled in the Online
1828  // to 1000; this will be removed again when the performance
1829  // bottlenecks are sorted out
1830  if ( comphelper::LibreOfficeKit::isActive() && nPosY > MAXTILEDROW - 1 )
1831  {
1832  nButtonDown = 0;
1834  return;
1835  }
1836 
1837  // Auto filter / pivot table / data select popup. This shouldn't activate the part.
1838 
1839  if ( !bDouble && !bFormulaMode && rMEvt.IsLeft() )
1840  {
1841  SCCOL nRealPosX;
1842  SCROW nRealPosY;
1843  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nRealPosX, nRealPosY, false );//the real row/col
1844 
1845  // show in the merged cells the filter of the first cell (nPosX instead of nRealPosX)
1846  const ScMergeFlagAttr* pRealPosAttr = rDoc.GetAttr(nPosX, nRealPosY, nTab, ATTR_MERGE_FLAG);
1847  if( pRealPosAttr->HasAutoFilter() )
1848  {
1849  SC_MOD()->InputEnterHandler();
1850  if (DoAutoFilterButton(nPosX, nRealPosY, rMEvt))
1851  return;
1852  }
1853 
1854  const ScMergeFlagAttr* pAttr = rDoc.GetAttr(nPosX, nPosY, nTab, ATTR_MERGE_FLAG);
1855  if (pAttr->HasAutoFilter())
1856  {
1857  if (DoAutoFilterButton(nPosX, nPosY, rMEvt))
1858  {
1859  rState.mbActivatePart = false;
1860  return;
1861  }
1862  }
1863 
1864  if (pAttr->HasPivotButton() || pAttr->HasPivotPopupButton())
1865  {
1866  DoPushPivotButton(nPosX, nPosY, rMEvt, pAttr->HasPivotButton(), pAttr->HasPivotPopupButton());
1867  rState.mbActivatePart = false;
1868  return;
1869  }
1870 
1871  // List Validity drop-down button
1872 
1873  if ( bListValButton )
1874  {
1876  if ( aButtonRect.Contains( aPos ) )
1877  {
1878  // tdf#125917 typically we have the mouse captured already, except if are editing the cell.
1879  // Ensure its captured before the menu is launched even in the cell editing case
1880  CaptureMouse();
1881 
1883 
1884  nMouseStatus = SC_GM_FILTER; // not set in DoAutoFilterMenue for bDataSelect
1885  rState.mbActivatePart = false;
1886  return;
1887  }
1888  }
1889  }
1890 
1891  // scenario selection
1892 
1893  ScRange aScenRange;
1894  if ( rMEvt.IsLeft() && HasScenarioButton( aPos, aScenRange ) )
1895  {
1896  CaptureMouse();
1897 
1898  DoScenarioMenu( aScenRange );
1899 
1900  // Scenario selection comes from MouseButtonDown:
1901  // The next MouseMove on the FilterBox is like a ButtonDown
1903  return;
1904  }
1905 
1906  // double click started ?
1907 
1908  // StopMarking can be called from DrawMouseButtonDown
1909 
1910  if ( nMouseStatus != SC_GM_IGNORE && !bRefMode )
1911  {
1912  if ( bDouble && !bCrossPointer )
1913  {
1914  if (nMouseStatus == SC_GM_TABDOWN)
1916  }
1917  else
1919  }
1920 
1921  // links in the edit cell
1922 
1923  bool bAlt = rMEvt.IsMod2();
1924  if ( !bAlt && rMEvt.IsLeft() && ScGlobal::ShouldOpenURL() &&
1925  GetEditUrl(rMEvt.GetPosPixel()) ) // click on link: do not move cursor
1926  {
1927  SetPointer( PointerStyle::RefHand );
1928  nMouseStatus = SC_GM_URLDOWN; // also only execute when ButtonUp
1929  return;
1930  }
1931 
1932  // Gridwin - Selection Engine
1933 
1934  if ( !rMEvt.IsLeft() )
1935  return;
1936 
1938  pSelEng->SetWindow(this);
1939  pSelEng->SetWhich(eWhich);
1941 
1942  // SelMouseButtonDown on the View is still setting the bMoveIsShift flag
1943  if ( mrViewData.GetView()->SelMouseButtonDown( rMEvt ) )
1944  {
1945  if (IsMouseCaptured())
1946  {
1947  // Tracking instead of CaptureMouse, so it can be canceled cleanly
1949  ReleaseMouse();
1950  StartTracking();
1951  }
1953  return;
1954  }
1955 }
1956 
1958 {
1959  aCurMousePos = rMEvt.GetPosPixel();
1960  ScDocument& rDoc = mrViewData.GetDocument();
1961  ScMarkData& rMark = mrViewData.GetMarkData();
1962  // #i41690# detect a MouseButtonUp call from within MouseButtonDown
1963  // (possible through Reschedule from storing an OLE object that is deselected)
1964 
1967 
1968  if (nButtonDown != rMEvt.GetButtons())
1969  nMouseStatus = SC_GM_IGNORE; // reset and return
1970 
1971  nButtonDown = 0;
1972 
1973  if (nMouseStatus == SC_GM_IGNORE)
1974  {
1976  // Selection engine: cancel selection
1978  rMark.SetMarking(false);
1979  if (mrViewData.IsAnyFillMode())
1980  {
1983  }
1984  StopMarking();
1985  DrawEndAction(); // cancel selection/moving in drawing layer
1986  ReleaseMouse();
1987  return;
1988  }
1989 
1990  if (nMouseStatus == SC_GM_FILTER)
1991  {
1993  ReleaseMouse();
1994  return; // nothing more should happen here
1995  }
1996 
1997  ScModule* pScMod = SC_MOD();
1998  if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
1999  return;
2000 
2001  SfxBindings& rBindings = mrViewData.GetBindings();
2002  if (bEEMouse && mrViewData.HasEditView( eWhich ))
2003  {
2004  EditView* pEditView;
2005  SCCOL nEditCol;
2006  SCROW nEditRow;
2007  mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2008  pEditView->MouseButtonUp( rMEvt );
2009 
2010  if ( rMEvt.IsMiddle() &&
2011  GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
2012  {
2013  // EditView may have pasted from selection
2014  pScMod->InputChanged( pEditView );
2015  }
2016  else
2017  pScMod->InputSelection( pEditView ); // parentheses etc.
2018 
2020  rBindings.Invalidate( SID_HYPERLINK_GETLINK );
2021  bEEMouse = false;
2022  return;
2023  }
2024 
2025  if (bDPMouse)
2026  {
2027  DPMouseButtonUp( rMEvt ); // resets bDPMouse
2028  return;
2029  }
2030 
2031  if (bRFMouse)
2032  {
2033  RFMouseMove( rMEvt, true ); // Again the proper range
2034  bRFMouse = false;
2035  SetPointer( PointerStyle::Arrow );
2036  ReleaseMouse();
2037  return;
2038  }
2039 
2040  if (nPagebreakMouse)
2041  {
2042  PagebreakMove( rMEvt, true );
2044  SetPointer( PointerStyle::Arrow );
2045  ReleaseMouse();
2046  return;
2047  }
2048 
2049  if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode
2050  {
2052  if ( pMgr->GetUndoActionCount() && dynamic_cast<ScUndoSelectionStyle*>(pMgr->GetUndoAction()) )
2053  pMgr->Undo();
2054  return;
2055  }
2056 
2057  if (DrawMouseButtonUp(rMEvt)) // includes format paint brush handling for drawing objects
2058  {
2059  ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2060  SfxBindings& rFrmBindings=pViewShell->GetViewFrame()->GetBindings();
2061  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_WIDTH);
2062  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_HEIGHT);
2063  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
2064  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
2065  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ANGLE);
2066  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_X);
2067  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_Y);
2068  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH);
2069  rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT);
2070  return;
2071  }
2072 
2073  rMark.SetMarking(false);
2074 
2075  SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
2076 
2077  if (mrViewData.IsFillMode() ||
2078  ( mrViewData.GetFillMode() == ScFillMode::MATRIX && rMEvt.IsMod1() ))
2079  {
2081  SCCOL nStartCol;
2082  SCROW nStartRow;
2083  SCCOL nEndCol;
2084  SCROW nEndRow;
2085  mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2086  ScRange aDelRange;
2087  bool bIsDel = mrViewData.GetDelMark( aDelRange );
2088 
2089  ScViewFunc* pView = mrViewData.GetView();
2090  pView->StopRefMode();
2092  pView->GetFunctionSet().SetAnchorFlag( false ); // #i5819# don't use AutoFill anchor flag for selection
2093 
2094  if ( bIsDel )
2095  {
2096  pView->MarkRange( aDelRange, false );
2098  SCTAB nTab = mrViewData.GetTabNo();
2099  ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
2100  if ( aBlockRange != aDelRange )
2101  {
2102  if ( aDelRange.aStart.Row() == nStartRow )
2103  aBlockRange.aEnd.SetCol( aDelRange.aStart.Col() - 1 );
2104  else
2105  aBlockRange.aEnd.SetRow( aDelRange.aStart.Row() - 1 );
2106  pView->MarkRange( aBlockRange, false );
2107  }
2108  }
2109  else
2110  mrViewData.GetDispatcher().Execute( FID_FILL_AUTO, SfxCallMode::SLOT | SfxCallMode::RECORD );
2111  }
2113  {
2114  SCTAB nTab = mrViewData.GetTabNo();
2115  SCCOL nStartCol;
2116  SCROW nStartRow;
2117  SCCOL nEndCol;
2118  SCROW nEndRow;
2119  mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
2120  ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
2121  SCCOL nFillCol = mrViewData.GetRefEndX();
2122  SCROW nFillRow = mrViewData.GetRefEndY();
2123  ScAddress aEndPos( nFillCol, nFillRow, nTab );
2124 
2125  ScTabView* pView = mrViewData.GetView();
2126  pView->StopRefMode();
2128  pView->GetFunctionSet().SetAnchorFlag( false );
2129 
2130  if ( aEndPos != aBlockRange.aEnd )
2131  {
2132  mrViewData.GetDocShell()->GetDocFunc().ResizeMatrix( aBlockRange, aEndPos );
2133  mrViewData.GetView()->MarkRange( ScRange( aBlockRange.aStart, aEndPos ) );
2134  }
2135  }
2136  else if (mrViewData.IsAnyFillMode())
2137  {
2138  // Embedded area has been changed
2139  ScTabView* pView = mrViewData.GetView();
2140  pView->StopRefMode();
2142  pView->GetFunctionSet().SetAnchorFlag( false );
2144  }
2145 
2146  bool bRefMode = mrViewData.IsRefMode();
2147  if (bRefMode)
2148  pScMod->EndReference();
2149 
2150  // Format paintbrush mode (Switch)
2151 
2152  if (pScMod->GetIsWaterCan())
2153  {
2154  // Check on undo already done above
2155 
2156  ScStyleSheetPool* pStylePool = mrViewData.GetDocument().
2157  GetStyleSheetPool();
2158  if ( pStylePool )
2159  {
2160  SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(
2161  pStylePool->GetActualStyleSheet());
2162 
2163  if ( pStyleSheet )
2164  {
2165  SfxStyleFamily eFamily = pStyleSheet->GetFamily();
2166 
2167  switch ( eFamily )
2168  {
2169  case SfxStyleFamily::Para:
2170  mrViewData.GetView()->SetStyleSheetToMarked( pStyleSheet );
2172  break;
2173 
2174  case SfxStyleFamily::Page:
2176  pStyleSheet->GetName() );
2177 
2180  mrViewData.GetTabNo() ).UpdatePages();
2181 
2182  rBindings.Invalidate( SID_STATUS_PAGESTYLE );
2183  break;
2184 
2185  default:
2186  break;
2187  }
2188  }
2189  }
2190  }
2191 
2192  ScDBFunc* pView = mrViewData.GetView();
2193  ScDocument* pBrushDoc = pView->GetBrushDocument();
2194  if ( pBrushDoc )
2195  {
2196  pView->PasteFromClip( InsertDeleteFlags::ATTRIB, pBrushDoc );
2197  if ( !pView->IsPaintBrushLocked() )
2198  pView->ResetBrushDocument(); // invalidates pBrushDoc pointer
2199  }
2200 
2201  Point aPos = rMEvt.GetPosPixel();
2202  SCCOL nPosX;
2203  SCROW nPosY;
2204  SCTAB nTab = mrViewData.GetTabNo();
2205  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2206  ScDPObject* pDPObj = rDoc.GetDPAtCursor( nPosX, nPosY, nTab );
2207 
2208  bool bInDataPilotTable = (pDPObj != nullptr);
2209 
2210  // double click (only left button)
2211  // in the tiled rendering case, single click works this way too
2212 
2213  bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
2214  bool bDouble = ( rMEvt.GetClicks() == 2 && rMEvt.IsLeft() );
2215  if ((bDouble || (bIsTiledRendering && !bInDataPilotTable))
2216  && !bRefMode
2217  && (nMouseStatus == SC_GM_DBLDOWN || (bIsTiledRendering && nMouseStatus != SC_GM_URLDOWN))
2218  && !pScMod->IsRefDialogOpen())
2219  {
2220  // data pilot table
2221  if ( pDPObj && pDPObj->GetSaveData()->GetDrillDown() )
2222  {
2223  ScAddress aCellPos( nPosX, nPosY, mrViewData.GetTabNo() );
2224 
2225  // Check for header drill-down first.
2226  sheet::DataPilotTableHeaderData aData;
2227  pDPObj->GetHeaderPositionData(aCellPos, aData);
2228 
2229  if ( ( aData.Flags & sheet::MemberResultFlags::HASMEMBER ) &&
2230  ! ( aData.Flags & sheet::MemberResultFlags::SUBTOTAL ) )
2231  {
2232  css::sheet::DataPilotFieldOrientation nDummy;
2233  if ( pView->HasSelectionForDrillDown( nDummy ) )
2234  {
2235  // execute slot to show dialog
2236  mrViewData.GetDispatcher().Execute( SID_OUTLINE_SHOW, SfxCallMode::SLOT | SfxCallMode::RECORD );
2237  }
2238  else
2239  {
2240  // toggle single entry
2241  ScDPObject aNewObj( *pDPObj );
2242  pDPObj->ToggleDetails( aData, &aNewObj );
2243  ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
2244  aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
2245  mrViewData.GetView()->CursorPosChanged(); // shells may be switched
2246  }
2247  }
2248  else
2249  {
2250  // Check if the data area is double-clicked.
2251 
2252  Sequence<sheet::DataPilotFieldFilter> aFilters;
2253  if ( pDPObj->GetDataFieldPositionData(aCellPos, aFilters) )
2254  mrViewData.GetView()->ShowDataPilotSourceData( *pDPObj, aFilters );
2255  }
2256 
2257  return;
2258  }
2259 
2260  // Check for cell protection attribute.
2261  const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
2262  bool bEditAllowed = true;
2263  if ( pProtect && pProtect->isProtected() )
2264  {
2265  bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
2266  bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
2267  bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
2268 
2269  if ( bSkipProtected && bSkipUnprotected )
2270  bEditAllowed = false;
2271  else if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
2272  bEditAllowed = false;
2273  }
2274 
2275  // We don't want to activate the edit view for a single click in tiled rendering
2276  // (but we should probably keep the same behaviour for double clicks).
2277  if ( bEditAllowed && (!bIsTiledRendering || bDouble) )
2278  {
2279  // don't forward the event to an empty cell, causes deselection in
2280  // case we used the double-click to select the empty cell
2281  if (bIsTiledRendering && bDouble)
2282  {
2283  ScRefCellValue aCell(mrViewData.GetDocument(), ScAddress(nPosX, nPosY, nTab));
2284  if (aCell.isEmpty())
2285  return;
2286  }
2287 
2288  // edit cell contents
2290  pScMod->SetInputMode( SC_INPUT_TABLE );
2291  if (mrViewData.HasEditView(eWhich))
2292  {
2293  // Set text cursor where clicked
2294  EditView* pEditView = mrViewData.GetEditView( eWhich );
2295  MouseEvent aEditEvt( rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 );
2296  pEditView->MouseButtonDown( aEditEvt );
2297  pEditView->MouseButtonUp( aEditEvt );
2298  }
2299  }
2300 
2301  if ( bIsTiledRendering && rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt ) )
2302  {
2304  }
2305 
2306  if ( bDouble )
2307  return;
2308  }
2309 
2310  // Links in edit cells
2311 
2312  bool bAlt = rMEvt.IsMod2();
2313  if ( !bAlt && !bRefMode && !bDouble && nMouseStatus == SC_GM_URLDOWN )
2314  {
2315  // Only execute on ButtonUp, if ButtonDown also was done on a URL
2316 
2317  OUString aName, aUrl, aTarget;
2318  if ( GetEditUrl( rMEvt.GetPosPixel(), &aName, &aUrl, &aTarget ) )
2319  {
2320  nMouseStatus = SC_GM_NONE; // Ignore double-click
2321  bool isTiledRendering = comphelper::LibreOfficeKit::isActive();
2322  // ScGlobal::OpenURL() only understands Calc A1 style syntax.
2323  // Convert it to Calc A1 before calling OpenURL().
2325  {
2326  if (aUrl.startsWith("#")) {
2327  ScGlobal::OpenURL(aUrl, aTarget, isTiledRendering);
2328  return;
2329  }
2330  // On a mobile device view there is no ctrl+click and for hyperlink popup
2331  // the cell coordinates must be sent along with click position for elegance
2332  ScTabViewShell* pViewShell = mrViewData.GetViewShell();
2333  if (isTiledRendering && pViewShell &&
2334  (pViewShell->isLOKMobilePhone() || pViewShell->isLOKTablet()))
2335  {
2336  aPos = rMEvt.GetPosPixel();
2337  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2338  OString aCursor = pViewShell->GetViewData().describeCellCursorAt(nPosX, nPosY);
2339  double fPPTX = pViewShell->GetViewData().GetPPTX();
2340  int mouseX = aPos.X() / fPPTX;
2341  OString aMsg(aUrl.toUtf8() + " coordinates: " + aCursor + ", " + OString::number(mouseX));
2342  pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg.getStr());
2343  } else
2344  ScGlobal::OpenURL(aUrl, aTarget);
2345  }
2346  else
2347  {
2348  ScAddress aTempAddr;
2349  ScAddress::ExternalInfo aExtInfo;
2350  ScRefFlags nRes = aTempAddr.Parse(aUrl, rDoc, rDoc.GetAddressConvention(), &aExtInfo);
2351  if (!(nRes & ScRefFlags::VALID))
2352  {
2353  // Not a reference string. Pass it through unmodified.
2354  ScGlobal::OpenURL(aUrl, aTarget);
2355  return;
2356  }
2357 
2358  OUStringBuffer aBuf;
2359  if (aExtInfo.mbExternal)
2360  {
2361  // External reference.
2362  ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
2363  const OUString* pStr = pRefMgr->getExternalFileName(aExtInfo.mnFileId);
2364  if (pStr)
2365  aBuf.append(*pStr);
2366 
2367  aBuf.append('#');
2368  aBuf.append(aExtInfo.maTabName);
2369  aBuf.append('.');
2370  OUString aRefCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS, nullptr, formula::FormulaGrammar::CONV_OOO));
2371  aBuf.append(aRefCalcA1);
2372  ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget);
2373  }
2374  else
2375  {
2376  // Internal reference.
2377  aBuf.append('#');
2378  OUString aUrlCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, formula::FormulaGrammar::CONV_OOO));
2379  aBuf.append(aUrlCalcA1);
2380  ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget, isTiledRendering);
2381  }
2382  }
2383 
2384  // fire worksheet_followhyperlink event
2385  uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = rDoc.GetVbaEventProcessor();
2386  if( xVbaEvents.is() ) try
2387  {
2388  aPos = rMEvt.GetPosPixel();
2389  nTab = mrViewData.GetTabNo();
2390  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2391  OUString sURL;
2392  ScRefCellValue aCell;
2393  if (lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
2394  {
2395  ScAddress aCellPos( nPosX, nPosY, nTab );
2396  uno::Reference< table::XCell > xCell( new ScCellObj( mrViewData.GetDocShell(), aCellPos ) );
2397  uno::Sequence< uno::Any > aArgs(1);
2398  aArgs[0] <<= xCell;
2399  xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_FOLLOWHYPERLINK, aArgs );
2400  }
2401  }
2402  catch( uno::Exception& )
2403  {
2404  }
2405 
2406  return;
2407  }
2408  }
2409 
2410  // Gridwin - SelectionEngine
2411 
2412  // SelMouseButtonDown is called only for left button, but SelMouseButtonUp would return
2413  // sal_True for any call, so IsLeft must be checked here, too.
2414 
2415  if ( !(rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt )) )
2416  return;
2417 
2419 
2421  bool bFormulaMode = pScMod->IsFormulaMode();
2422  OSL_ENSURE( pDisp || bFormulaMode, "Cursor moved on inactive View ?" );
2423 
2424  // #i14927# execute SID_CURRENTCELL (for macro recording) only if there is no
2425  // multiple selection, so the argument string completely describes the selection,
2426  // and executing the slot won't change the existing selection (executing the slot
2427  // here and from a recorded macro is treated equally)
2428  if ( pDisp && !bFormulaMode && !rMark.IsMultiMarked() )
2429  {
2430  OUString aAddr; // CurrentCell
2431  if( rMark.IsMarked() )
2432  {
2433  ScRange aScRange;
2434  rMark.GetMarkArea( aScRange );
2435  aAddr = aScRange.Format(rDoc, ScRefFlags::RANGE_ABS);
2436  if ( aScRange.aStart == aScRange.aEnd )
2437  {
2438  // make sure there is a range selection string even for a single cell
2439  aAddr += ":" + aAddr;
2440  }
2441 
2444  }
2445  else // only move cursor
2446  {
2447  ScAddress aScAddress( mrViewData.GetCurX(), mrViewData.GetCurY(), 0 );
2448  aAddr = aScAddress.Format(ScRefFlags::ADDR_ABS);
2449  }
2450 
2451  SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
2452  // We don't want to align to the cursor position because if the
2453  // cell cursor isn't visible after making selection, it would jump
2454  // back to the origin of the selection where the cell cursor is.
2455  SfxBoolItem aAlignCursorItem( FN_PARAM_2, false );
2456  pDisp->ExecuteList(SID_CURRENTCELL,
2457  SfxCallMode::SLOT | SfxCallMode::RECORD,
2458  { &aPosItem, &aAlignCursorItem });
2459 
2461 
2462  }
2464 
2465  return;
2466 }
2467 
2469 {
2470  if ( nButtonDown )
2471  {
2472  MouseEvent aEvent( aCurMousePos ); // nButtons = 0 -> ignore
2473  MouseButtonUp( aEvent );
2474  }
2475 }
2476 
2478 {
2479  aCurMousePos = rMEvt.GetPosPixel();
2480 
2481  if (rMEvt.IsLeaveWindow() && mpNoteMarker && !mpNoteMarker->IsByKeyboard())
2482  HideNoteMarker();
2483 
2484  ScModule* pScMod = SC_MOD();
2485  if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
2486  return;
2487 
2488  // If the Drag&Drop is started in the edit mode then sadly nothing else is kept
2489  if (bEEMouse && nButtonDown && !rMEvt.GetButtons())
2490  {
2491  bEEMouse = false;
2492  nButtonDown = 0;
2494  return;
2495  }
2496 
2497  if (nMouseStatus == SC_GM_IGNORE)
2498  return;
2499 
2500  if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode -> only what for Up
2501  return;
2502 
2503  if ( mrViewData.GetViewShell()->IsAuditShell() ) // Detective Fill Mode
2504  {
2505  SetPointer( PointerStyle::Fill );
2506  return;
2507  }
2508 
2509  bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
2510 
2511  if (bEEMouse && mrViewData.HasEditView( eWhich ))
2512  {
2513  EditView* pEditView;
2514  SCCOL nEditCol;
2515  SCROW nEditRow;
2516  mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2517  pEditView->MouseMove( rMEvt );
2518  return;
2519  }
2520 
2521  if (bDPMouse)
2522  {
2523  DPMouseMove( rMEvt );
2524  return;
2525  }
2526 
2527  if (bRFMouse)
2528  {
2529  RFMouseMove( rMEvt, false );
2530  return;
2531  }
2532 
2533  if (nPagebreakMouse)
2534  {
2535  PagebreakMove( rMEvt, false );
2536  return;
2537  }
2538 
2539  // Show other mouse pointer?
2540 
2541  bool bEditMode = mrViewData.HasEditView(eWhich);
2542 
2544  if ( bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()) )
2545  {
2546  Point aPos = rMEvt.GetPosPixel();
2547  SCCOL nPosX;
2548  SCROW nPosY;
2549  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
2550 
2551  EditView* pEditView;
2552  SCCOL nEditCol;
2553  SCROW nEditRow;
2554  mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2555  SCCOL nEndCol = mrViewData.GetEditEndCol();
2556  SCROW nEndRow = mrViewData.GetEditEndRow();
2557 
2558  if ( nPosX >= nEditCol && nPosX <= nEndCol &&
2559  nPosY >= nEditRow && nPosY <= nEndRow )
2560  {
2561  if ( !pEditView )
2562  {
2563  SetPointer( PointerStyle::Text );
2564  return;
2565  }
2566 
2567  const SvxFieldItem* pFld;
2569  {
2570  Point aLogicClick = pEditView->GetOutputDevice().PixelToLogic(aPos);
2571  pFld = pEditView->GetField( aLogicClick );
2572  }
2573  else
2574  {
2575  pFld = pEditView->GetFieldUnderMousePointer();
2576  }
2577  // Field can only be URL field
2578  bool bAlt = rMEvt.IsMod2();
2579  if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() && pFld )
2580  SetPointer( PointerStyle::RefHand );
2581  else if ( pEditView->GetEditEngine()->IsEffectivelyVertical() )
2582  SetPointer( PointerStyle::TextVertical );
2583  else
2584  SetPointer( PointerStyle::Text );
2585  return;
2586  }
2587  }
2588 
2589  bool bWater = SC_MOD()->GetIsWaterCan() || mrViewData.GetView()->HasPaintBrush();
2590  if (bWater)
2591  SetPointer( PointerStyle::Fill );
2592 
2593  if (!bWater)
2594  {
2595  bool bCross = false;
2596 
2597  // range finder
2598 
2599  RfCorner rCorner = NONE;
2600  if ( HitRangeFinder( rMEvt.GetPosPixel(), rCorner, nullptr, nullptr, nullptr ) )
2601  {
2602  if (rCorner != NONE)
2603  SetPointer( PointerStyle::Cross );
2604  else
2605  SetPointer( PointerStyle::Hand );
2606  bCross = true;
2607  }
2608 
2609  // Page-Break-Mode
2610 
2612  {
2613  sal_uInt16 nBreakType = HitPageBreak( rMEvt.GetPosPixel(), nullptr, nullptr, nullptr );
2614  if (nBreakType != 0 )
2615  {
2616  PointerStyle eNew = PointerStyle::Arrow;
2617  switch ( nBreakType )
2618  {
2619  case SC_PD_RANGE_L:
2620  case SC_PD_RANGE_R:
2621  case SC_PD_BREAK_H:
2622  eNew = PointerStyle::ESize;
2623  break;
2624  case SC_PD_RANGE_T:
2625  case SC_PD_RANGE_B:
2626  case SC_PD_BREAK_V:
2627  eNew = PointerStyle::SSize;
2628  break;
2629  case SC_PD_RANGE_TL:
2630  case SC_PD_RANGE_BR:
2631  eNew = PointerStyle::SESize;
2632  break;
2633  case SC_PD_RANGE_TR:
2634  case SC_PD_RANGE_BL:
2635  eNew = PointerStyle::NESize;
2636  break;
2637  }
2638  SetPointer( eNew );
2639  bCross = true;
2640  }
2641  }
2642 
2643  // Show fill cursor?
2644 
2645  if ( !bFormulaMode && !nButtonDown )
2646  if (TestMouse( rMEvt, false ))
2647  bCross = true;
2648 
2650  {
2651  SetPointer( PointerStyle::Cross );
2652  bCross = true;
2653  nScFillModeMouseModifier = rMEvt.GetModifier(); // evaluated for AutoFill and Matrix
2654  }
2655 
2656  if (!bCross)
2657  {
2658  bool bAlt = rMEvt.IsMod2();
2659 
2660  if (bEditMode) // First has to be in edit mode!
2661  SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
2662  else if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() &&
2663  GetEditUrl(rMEvt.GetPosPixel()) )
2664  SetPointer( PointerStyle::RefHand );
2665  else if ( DrawMouseMove(rMEvt) ) // Reset pointer
2666  return;
2667  }
2668  }
2669 
2670  // In LOK case, avoid spurious "leavingwindow" mouse move events which has negative coordinates.
2671  // Such events occur for some reason when a user is selecting a range, (even when not leaving the view area)
2672  // with one or more other viewers in that sheet.
2673  bool bSkipSelectionUpdate = comphelper::LibreOfficeKit::isActive() &&
2674  rMEvt.IsLeaveWindow() && (aCurMousePos.X() < 0 || aCurMousePos.Y() < 0);
2675 
2676  if (!bSkipSelectionUpdate)
2678 }
2679 
2680 static void lcl_InitMouseEvent(css::awt::MouseEvent& rEvent, const MouseEvent& rEvt)
2681 {
2682  rEvent.Modifiers = 0;
2683  if ( rEvt.IsShift() )
2684  rEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
2685  if ( rEvt.IsMod1() )
2686  rEvent.Modifiers |= css::awt::KeyModifier::MOD1;
2687  if ( rEvt.IsMod2() )
2688  rEvent.Modifiers |= css::awt::KeyModifier::MOD2;
2689  if ( rEvt.IsMod3() )
2690  rEvent.Modifiers |= css::awt::KeyModifier::MOD3;
2691 
2692  rEvent.Buttons = 0;
2693  if ( rEvt.IsLeft() )
2694  rEvent.Buttons |= css::awt::MouseButton::LEFT;
2695  if ( rEvt.IsRight() )
2696  rEvent.Buttons |= css::awt::MouseButton::RIGHT;
2697  if ( rEvt.IsMiddle() )
2698  rEvent.Buttons |= css::awt::MouseButton::MIDDLE;
2699 
2700  rEvent.X = rEvt.GetPosPixel().X();
2701  rEvent.Y = rEvt.GetPosPixel().Y();
2702  rEvent.ClickCount = rEvt.GetClicks();
2703  rEvent.PopupTrigger = false;
2704 }
2705 
2707 {
2708  bool bDone = false;
2709  MouseNotifyEvent nType = rNEvt.GetType();
2710  if ( nType == MouseNotifyEvent::MOUSEBUTTONUP || nType == MouseNotifyEvent::MOUSEBUTTONDOWN )
2711  {
2712  vcl::Window* pWindow = rNEvt.GetWindow();
2713  if (pWindow == this)
2714  {
2715  SfxViewFrame* pViewFrame = mrViewData.GetViewShell()->GetViewFrame();
2716  if (pViewFrame)
2717  {
2718  css::uno::Reference<css::frame::XController> xController = pViewFrame->GetFrame().GetController();
2719  if (xController.is())
2720  {
2721  ScTabViewObj* pImp = comphelper::getFromUnoTunnel<ScTabViewObj>( xController );
2722  if (pImp && pImp->IsMouseListening())
2723  {
2724  css::awt::MouseEvent aEvent;
2725  lcl_InitMouseEvent( aEvent, *rNEvt.GetMouseEvent() );
2726  if ( rNEvt.GetWindow() )
2727  aEvent.Source = rNEvt.GetWindow()->GetComponentInterface();
2728  if ( nType == MouseNotifyEvent::MOUSEBUTTONDOWN)
2729  bDone = pImp->MousePressed( aEvent );
2730  else
2731  bDone = pImp->MouseReleased( aEvent );
2732  }
2733  }
2734  }
2735  }
2736  }
2737  if (bDone) // event consumed by a listener
2738  {
2739  if ( nType == MouseNotifyEvent::MOUSEBUTTONDOWN )
2740  {
2741  const MouseEvent* pMouseEvent = rNEvt.GetMouseEvent();
2742  if ( pMouseEvent->IsRight() && pMouseEvent->GetClicks() == 1 )
2743  {
2744  // If a listener returned true for a right-click call, also prevent opening the context menu
2745  // (this works only if the context menu is opened on mouse-down)
2747  }
2748  }
2749 
2750  return true;
2751  }
2752  else
2753  return Window::PreNotify( rNEvt );
2754 }
2755 
2757 {
2758  // Since the SelectionEngine does not track, the events have to be
2759  // handed to the different MouseHandler...
2760 
2761  const MouseEvent& rMEvt = rTEvt.GetMouseEvent();
2762 
2763  if ( rTEvt.IsTrackingCanceled() ) // Cancel everything...
2764  {
2765  if (!mrViewData.GetView()->IsInActivatePart() && !SC_MOD()->IsRefDialogOpen())
2766  {
2767  if (bDPMouse)
2768  bDPMouse = false; // Paint for each bDragRect
2769  if (bDragRect)
2770  {
2771  bDragRect = false;
2773  }
2774  if (bRFMouse)
2775  {
2776  RFMouseMove( rMEvt, true ); // Not possible to cancel properly...
2777  bRFMouse = false;
2778  }
2779  if (nPagebreakMouse)
2780  {
2781  bPagebreakDrawn = false;
2784  }
2785 
2786  SetPointer( PointerStyle::Arrow );
2787  StopMarking();
2788  MouseButtonUp( rMEvt ); // With status SC_GM_IGNORE from StopMarking
2789 
2790  bool bRefMode = mrViewData.IsRefMode();
2791  if (bRefMode)
2792  SC_MOD()->EndReference(); // Do not let the Dialog remain minimized
2793  }
2794  }
2795  else if ( rTEvt.IsTrackingEnded() )
2796  {
2798  {
2799  // MouseButtonUp always with matching buttons (eg for test tool, # 63148 #)
2800  // The tracking event will indicate if it was completed and not canceled.
2801  MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
2802  rMEvt.GetMode(), nButtonDown, rMEvt.GetModifier() );
2803  MouseButtonUp( aUpEvt );
2804  }
2805  }
2807  MouseMove( rMEvt );
2808 }
2809 
2810 void ScGridWindow::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
2811 {
2813  return;
2814 
2815  HideNoteMarker();
2816 
2817  CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
2818 
2819  if (bEEMouse && mrViewData.HasEditView( eWhich ))
2820  {
2821  EditView* pEditView;
2822  SCCOL nEditCol;
2823  SCROW nEditRow;
2824  mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
2825 
2826  // don't remove the edit view while switching views
2827  ScModule* pScMod = SC_MOD();
2828  pScMod->SetInEditCommand( true );
2829 
2830  pEditView->Command( aDragEvent );
2831 
2832  ScInputHandler* pHdl = pScMod->GetInputHdl();
2833  if (pHdl)
2834  pHdl->DataChanged();
2835 
2836  pScMod->SetInEditCommand( false );
2837  if (!mrViewData.IsActive()) // dropped to different view?
2838  {
2839  ScInputHandler* pViewHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
2840  if ( pViewHdl && mrViewData.HasEditView( eWhich ) )
2841  {
2842  pViewHdl->CancelHandler();
2843  ShowCursor(); // missing from KillEditView
2844  }
2845  }
2846  }
2847  else
2848  if ( !DrawCommand(aDragEvent) )
2849  mrViewData.GetView()->GetSelEngine()->Command( aDragEvent );
2850 }
2851 
2852 static void lcl_SetTextCursorPos( ScViewData& rViewData, ScSplitPos eWhich, vcl::Window* pWin )
2853 {
2854  SCCOL nCol = rViewData.GetCurX();
2855  SCROW nRow = rViewData.GetCurY();
2856  tools::Rectangle aEditArea = rViewData.GetEditArea( eWhich, nCol, nRow, pWin, nullptr, true );
2857  aEditArea.SetRight( aEditArea.Left() );
2858  aEditArea = pWin->PixelToLogic( aEditArea );
2859  pWin->SetCursorRect( &aEditArea );
2860 }
2861 
2863 {
2864  // The command event is send to the window after a possible context
2865  // menu from an inplace client is closed. Now we have the chance to
2866  // deactivate the inplace client without any problem regarding parent
2867  // windows and code on the stack.
2868  CommandEventId nCmd = rCEvt.GetCommand();
2869  ScTabViewShell* pTabViewSh = mrViewData.GetViewShell();
2870  SfxInPlaceClient* pClient = pTabViewSh->GetIPClient();
2871  if ( pClient &&
2872  pClient->IsObjectInPlaceActive() &&
2873  nCmd == CommandEventId::ContextMenu )
2874  {
2875  pTabViewSh->DeactivateOle();
2876  return;
2877  }
2878 
2879  ScModule* pScMod = SC_MOD();
2880  OSL_ENSURE( nCmd != CommandEventId::StartDrag, "ScGridWindow::Command called with CommandEventId::StartDrag" );
2881 
2882  if (nCmd == CommandEventId::ModKeyChange)
2883  {
2884  Window::Command(rCEvt);
2885  return;
2886  }
2887 
2888  if ( nCmd == CommandEventId::StartExtTextInput ||
2889  nCmd == CommandEventId::EndExtTextInput ||
2890  nCmd == CommandEventId::ExtTextInput ||
2891  nCmd == CommandEventId::CursorPos ||
2892  nCmd == CommandEventId::QueryCharPosition )
2893  {
2894  bool bEditView = mrViewData.HasEditView( eWhich );
2895  if (!bEditView)
2896  {
2897  // only if no cell editview is active, look at drawview
2898  SdrView* pSdrView = mrViewData.GetView()->GetScDrawView();
2899  if ( pSdrView )
2900  {
2901  OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
2902  if ( pOlView && pOlView->GetWindow() == this )
2903  {
2904  pOlView->Command( rCEvt );
2905  return; // done
2906  }
2907  }
2908  }
2909 
2910  if ( nCmd == CommandEventId::CursorPos && !bEditView )
2911  {
2912  // CURSORPOS may be called without following text input,
2913  // to set the input method window position
2914  // -> input mode must not be started,
2915  // manually calculate text insert position if not in input mode
2916 
2917  lcl_SetTextCursorPos( mrViewData, eWhich, this );
2918  return;
2919  }
2920 
2921  ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
2922  if ( pHdl )
2923  {
2924  pHdl->InputCommand( rCEvt );
2925  return; // done
2926  }
2927 
2928  Window::Command( rCEvt );
2929  return;
2930  }
2931 
2932  if ( nCmd == CommandEventId::PasteSelection )
2933  {
2934  if ( bEEMouse )
2935  {
2936  // EditEngine handles selection in MouseButtonUp - no action
2937  // needed in command handler
2938  }
2939  else
2940  {
2941  PasteSelection( rCEvt.GetMousePosPixel() );
2942  }
2943  return;
2944  }
2945 
2946  if ( nCmd == CommandEventId::InputLanguageChange )
2947  {
2948  // #i55929# Font and font size state depends on input language if nothing is selected,
2949  // so the slots have to be invalidated when the input language is changed.
2950 
2951  SfxBindings& rBindings = mrViewData.GetBindings();
2952  rBindings.Invalidate( SID_ATTR_CHAR_FONT );
2953  rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
2954  return;
2955  }
2956 
2957  if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
2958  {
2959  bool bDone = mrViewData.GetView()->ScrollCommand( rCEvt, eWhich );
2960  if (!bDone)
2961  Window::Command(rCEvt);
2962  return;
2963  }
2964  // #i7560# FormulaMode check is below scrolling - scrolling is allowed during formula input
2965  bool bDisable = pScMod->IsFormulaMode() ||
2967  if (bDisable)
2968  return;
2969 
2970  if (nCmd != CommandEventId::ContextMenu || SC_MOD()->GetIsWaterCan())
2971  return;
2972 
2973  bool bMouse = rCEvt.IsMouseEvent();
2974  if ( bMouse && nMouseStatus == SC_GM_IGNORE )
2975  return;
2976 
2977  if (mrViewData.IsAnyFillMode())
2978  {
2981  }
2982  ReleaseMouse();
2983  StopMarking();
2984 
2985  Point aPosPixel = rCEvt.GetMousePosPixel();
2986  Point aMenuPos = aPosPixel;
2987 
2988  SCCOL nCellX = -1;
2989  SCROW nCellY = -1;
2990  mrViewData.GetPosFromPixel(aPosPixel.X(), aPosPixel.Y(), eWhich, nCellX, nCellY);
2991 
2992  bool bSpellError = false;
2993  SCCOL nColSpellError = nCellX;
2994 
2995  if ( bMouse )
2996  {
2997  ScDocument& rDoc = mrViewData.GetDocument();
2998  SCTAB nTab = mrViewData.GetTabNo();
2999  const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
3000  bool bSelectAllowed = true;
3001  if ( pProtect && pProtect->isProtected() )
3002  {
3003  // This sheet is protected. Check if a context menu is allowed on this cell.
3004  bool bCellProtected = rDoc.HasAttrib(nCellX, nCellY, nTab, nCellX, nCellY, nTab, HasAttrFlags::Protected);
3005  bool bSelProtected = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
3006  bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
3007 
3008  if (bCellProtected)
3009  bSelectAllowed = bSelProtected;
3010  else
3011  bSelectAllowed = bSelUnprotected;
3012  }
3013  if (!bSelectAllowed)
3014  // Selecting this cell is not allowed, neither is context menu.
3015  return;
3016 
3017  if (mpSpellCheckCxt)
3018  {
3019  // Find the first string to the left for spell checking in case the current cell is empty.
3020  ScAddress aPos(nCellX, nCellY, nTab);
3021  ScRefCellValue aSpellCheckCell(rDoc, aPos);
3022  while (aSpellCheckCell.meType == CELLTYPE_NONE)
3023  {
3024  // Loop until we get the first non-empty cell in the row.
3025  aPos.IncCol(-1);
3026  if (aPos.Col() < 0)
3027  break;
3028 
3029  aSpellCheckCell.assign(rDoc, aPos);
3030  }
3031 
3032  if (aPos.Col() >= 0 && (aSpellCheckCell.meType == CELLTYPE_STRING || aSpellCheckCell.meType == CELLTYPE_EDIT))
3033  nColSpellError = aPos.Col();
3034 
3035  bSpellError = (mpSpellCheckCxt->isMisspelled(nColSpellError, nCellY));
3036  if (bSpellError)
3037  {
3038  // Check and see if a misspelled word is under the mouse pointer.
3039  bSpellError = IsSpellErrorAtPos(aPosPixel, nColSpellError, nCellY);
3040  }
3041  }
3042 
3043  // #i18735# First select the item under the mouse pointer.
3044  // This can change the selection, and the view state (edit mode, etc).
3045  SelectForContextMenu(aPosPixel, bSpellError ? nColSpellError : nCellX, nCellY);
3046  }
3047 
3048  bool bDone = false;
3049  bool bEdit = mrViewData.HasEditView(eWhich);
3050 
3051  if ( !bEdit )
3052  {
3053  // Edit cell with spelling errors ?
3054  if (bMouse && (GetEditUrl(aPosPixel) || bSpellError))
3055  {
3056  // GetEditUrlOrError has already moved the Cursor
3057 
3058  pScMod->SetInputMode( SC_INPUT_TABLE );
3059  bEdit = mrViewData.HasEditView(eWhich); // Did it work?
3060 
3061  OSL_ENSURE( bEdit, "Can not be switched in edit mode" );
3062  }
3063  }
3064  if ( bEdit )
3065  {
3066  EditView* pEditView = mrViewData.GetEditView( eWhich ); // is then not 0
3067 
3068  if ( !bMouse )
3069  {
3070  vcl::Cursor* pCur = pEditView->GetCursor();
3071  if ( pCur )
3072  {
3073  Point aLogicPos = pCur->GetPos();
3074  // use the position right of the cursor (spell popup is opened if
3075  // the cursor is before the word, but not if behind it)
3076  aLogicPos.AdjustX(pCur->GetWidth() );
3077  aLogicPos.AdjustY(pCur->GetHeight() / 2 ); // center vertically
3078  aMenuPos = LogicToPixel( aLogicPos );
3079  }
3080  }
3081 
3082  // if edit mode was just started above, online spelling may be incomplete
3083  pEditView->GetEditEngine()->CompleteOnlineSpelling();
3084 
3085  // IsCursorAtWrongSpelledWord could be used for !bMouse
3086  // if there was a corresponding ExecuteSpellPopup call
3087 
3088  if (bSpellError)
3089  {
3090  // On OS/2 when clicking next to the Popup menu, the MouseButtonDown
3091  // comes before the end of menu execute, thus the SetModified has to
3092  // be done prior to this (Bug #40968#)
3093  ScInputHandler* pHdl = pScMod->GetInputHdl();
3094  if (pHdl)
3095  pHdl->SetModified();
3096 
3097  Link<SpellCallbackInfo&,void> aLink = LINK( this, ScGridWindow, PopupSpellingHdl );
3098  pEditView->ExecuteSpellPopup(aMenuPos, aLink);
3099 
3100  bDone = true;
3101  }
3102  }
3103  else if ( !bMouse )
3104  {
3105  // non-edit menu by keyboard -> use lower right of cell cursor position
3106  ScDocument& rDoc = mrViewData.GetDocument();
3107  SCTAB nTabNo = mrViewData.GetTabNo();
3108  bool bLayoutIsRTL = rDoc.IsLayoutRTL(nTabNo);
3109 
3110  SCCOL nCurX = mrViewData.GetCurX();
3111  SCROW nCurY = mrViewData.GetCurY();
3112  aMenuPos = mrViewData.GetScrPos( nCurX, nCurY, eWhich, true );
3113  tools::Long nSizeXPix;
3114  tools::Long nSizeYPix;
3115  mrViewData.GetMergeSizePixel( nCurX, nCurY, nSizeXPix, nSizeYPix );
3116  // fdo#55432 take the correct position for RTL sheet
3117  aMenuPos.AdjustX(bLayoutIsRTL ? -nSizeXPix : nSizeXPix );
3118  aMenuPos.AdjustY(nSizeYPix );
3119 
3120  ScTabViewShell* pViewSh = mrViewData.GetViewShell();
3121  if (pViewSh)
3122  {
3123  // Is a draw object selected?
3124 
3125  SdrView* pDrawView = pViewSh->GetScDrawView();
3126  if (pDrawView && pDrawView->AreObjectsMarked())
3127  {
3128  // #100442#; the context menu should open in the middle of the selected objects
3129  tools::Rectangle aSelectRect(LogicToPixel(pDrawView->GetAllMarkedBoundRect()));
3130  aMenuPos = aSelectRect.Center();
3131  }
3132  }
3133  }
3134 
3135  if (!bDone)
3136  {
3137  // tdf#140361 at this context menu popup time get what the
3138  // DisableEditHyperlink would be for this position
3139  bool bShouldDisableEditHyperlink = mrViewData.GetViewShell()->ShouldDisableEditHyperlink();
3140 
3141  SfxDispatcher::ExecutePopup( this, &aMenuPos );
3142 
3143  if (!bShouldDisableEditHyperlink)
3144  {
3145  SfxBindings& rBindings = mrViewData.GetBindings();
3146  // tdf#140361 set what the menu popup state for this was
3148  // ensure moAtContextMenu_DisableEditHyperlink will be cleared
3149  // in the case that EditHyperlink is not dispatched by the menu
3150  rBindings.Invalidate(SID_EDIT_HYPERLINK);
3151  }
3152  }
3153 }
3154 
3155 void ScGridWindow::SelectForContextMenu( const Point& rPosPixel, SCCOL nCellX, SCROW nCellY )
3156 {
3157  // #i18735# if the click was outside of the current selection,
3158  // the cursor is moved or an object at the click position selected.
3159  // (see SwEditWin::SelectMenuPosition in Writer)
3160 
3161  ScTabView* pView = mrViewData.GetView();
3162  ScDrawView* pDrawView = pView->GetScDrawView();
3163 
3164  // check cell edit mode
3165 
3166  if ( mrViewData.HasEditView(eWhich) )
3167  {
3168  ScModule* pScMod = SC_MOD();
3169  SCCOL nEditStartCol = mrViewData.GetEditViewCol();
3170  SCROW nEditStartRow = mrViewData.GetEditViewRow();
3171  SCCOL nEditEndCol = mrViewData.GetEditEndCol();
3172  SCROW nEditEndRow = mrViewData.GetEditEndRow();
3173 
3174  if ( nCellX >= nEditStartCol && nCellX <= nEditEndCol &&
3175  nCellY >= nEditStartRow && nCellY <= nEditEndRow )
3176  {
3177  // handle selection within the EditView
3178 
3179  EditView* pEditView = mrViewData.GetEditView( eWhich ); // not NULL (HasEditView)
3180  EditEngine* pEditEngine = pEditView->GetEditEngine();
3181  tools::Rectangle aOutputArea = pEditView->GetOutputArea();
3182  tools::Rectangle aVisArea = pEditView->GetVisArea();
3183 
3184  Point aTextPos = PixelToLogic( rPosPixel );
3185  if ( pEditEngine->IsEffectivelyVertical() ) // have to manually transform position
3186  {
3187  aTextPos -= aOutputArea.TopRight();
3188  tools::Long nTemp = -aTextPos.X();
3189  aTextPos.setX( aTextPos.Y() );
3190  aTextPos.setY( nTemp );
3191  }
3192  else
3193  aTextPos -= aOutputArea.TopLeft();
3194  aTextPos += aVisArea.TopLeft(); // position in the edit document
3195 
3196  EPosition aDocPosition = pEditEngine->FindDocPosition(aTextPos);
3197  ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
3198  ESelection aSelection = pEditView->GetSelection();
3199  aSelection.Adjust(); // needed for IsLess/IsGreater
3200  if ( aCompare < aSelection || aCompare > aSelection )
3201  {
3202  // clicked outside the selected text - deselect and move text cursor
3203  MouseEvent aEvent( rPosPixel );
3204  pEditView->MouseButtonDown( aEvent );
3205  pEditView->MouseButtonUp( aEvent );
3206  pScMod->InputSelection( pEditView );
3207  }
3208 
3209  return; // clicked within the edit view - keep edit mode
3210  }
3211  else
3212  {
3213  // outside of the edit view - end edit mode, regardless of cell selection, then continue
3214  pScMod->InputEnterHandler();
3215  }
3216  }
3217 
3218  // check draw text edit mode
3219 
3220  Point aLogicPos = PixelToLogic( rPosPixel ); // after cell edit mode is ended
3221  if ( pDrawView && pDrawView->GetTextEditObject() && pDrawView->GetTextEditOutlinerView() )
3222  {
3223  OutlinerView* pOlView = pDrawView->GetTextEditOutlinerView();
3224  tools::Rectangle aOutputArea = pOlView->GetOutputArea();
3225  if ( aOutputArea.Contains( aLogicPos ) )
3226  {
3227  // handle selection within the OutlinerView
3228 
3229  Outliner* pOutliner = pOlView->GetOutliner();
3230  const EditEngine& rEditEngine = pOutliner->GetEditEngine();
3231  tools::Rectangle aVisArea = pOlView->GetVisArea();
3232 
3233  Point aTextPos = aLogicPos;
3234  if ( pOutliner->IsVertical() ) // have to manually transform position
3235  {
3236  aTextPos -= aOutputArea.TopRight();
3237  tools::Long nTemp = -aTextPos.X();
3238  aTextPos.setX( aTextPos.Y() );
3239  aTextPos.setY( nTemp );
3240  }
3241  else
3242  aTextPos -= aOutputArea.TopLeft();
3243  aTextPos += aVisArea.TopLeft(); // position in the edit document
3244 
3245  EPosition aDocPosition = rEditEngine.FindDocPosition(aTextPos);
3246  ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
3247  ESelection aSelection = pOlView->GetSelection();
3248  aSelection.Adjust(); // needed for IsLess/IsGreater
3249  if ( aCompare < aSelection || aCompare > aSelection )
3250  {
3251  // clicked outside the selected text - deselect and move text cursor
3252  // use DrawView to allow extra handling there (none currently)
3253  MouseEvent aEvent( rPosPixel );
3254  pDrawView->MouseButtonDown( aEvent, GetOutDev() );
3255  pDrawView->MouseButtonUp( aEvent, GetOutDev() );
3256  }
3257 
3258  return; // clicked within the edit area - keep edit mode
3259  }
3260  else
3261  {
3262  // Outside of the edit area - end text edit mode, then continue.
3263  // DrawDeselectAll also ends text edit mode and updates the shells.
3264  // If the click was on the edited object, it will be selected again below.
3265  pView->DrawDeselectAll();
3266  }
3267  }
3268 
3269  // look for existing selection
3270 
3271  bool bHitSelected = false;
3272  if ( pDrawView && pDrawView->IsMarkedObjHit( aLogicPos ) )
3273  {
3274  // clicked on selected object -> don't change anything
3275  bHitSelected = true;
3276  }
3277  else if ( mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY) )
3278  {
3279  // clicked on selected cell -> don't change anything
3280  bHitSelected = true;
3281  }
3282 
3283  // select drawing object or move cell cursor
3284 
3285  if ( bHitSelected )
3286  return;
3287 
3288  bool bWasDraw = ( pDrawView && pDrawView->AreObjectsMarked() );
3289  bool bHitDraw = false;
3290  if ( pDrawView )
3291  {
3292  pDrawView->UnmarkAllObj();
3293  // Unlock the Internal Layer in order to activate the context menu.
3294  // re-lock in ScDrawView::MarkListHasChanged()
3295  lcl_UnLockComment(pDrawView, aLogicPos, mrViewData);
3296  bHitDraw = pDrawView->MarkObj( aLogicPos );
3297  // draw shell is activated in MarkListHasChanged
3298  }
3299  if ( !bHitDraw )
3300  {
3301  pView->Unmark();
3302  pView->SetCursor(nCellX, nCellY);
3303  if ( bWasDraw )
3304  mrViewData.GetViewShell()->SetDrawShell( false ); // switch shells
3305  }
3306 }
3307 
3309 {
3310  // Cursor control for ref input dialog
3311  const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
3312 
3313 #ifdef DBG_UTIL
3314 
3315  if (rKeyCode.IsMod1() && rKeyCode.IsShift())
3316  {
3317  if (rKeyCode.GetCode() == KEY_F12)
3318  {
3320  }
3321  else if (rKeyCode.GetCode() == KEY_F11)
3322  {
3324  }
3325  else if (rKeyCode.GetCode() == KEY_F10)
3326  {
3328  }
3329  else if (rKeyCode.GetCode() == KEY_F6)
3330  {
3332  }
3333  else if (rKeyCode.GetCode() == KEY_F8)
3334  {
3336  }
3337  else if (rKeyCode.GetCode() == KEY_F7)
3338  {
3339  ScDocument& rDoc = mrViewData.GetDocument();
3340  auto& rMapper = rDoc.GetExternalDataMapper();
3341  for (auto& itr : rMapper.getDataSources())
3342  {
3343  itr.refresh(&rDoc);
3344  }
3345  return;
3346  }
3347  }
3348 
3349 #endif
3350 
3351  if( SC_MOD()->IsRefDialogOpen() )
3352  {
3353  if( !rKeyCode.GetModifier() && (rKeyCode.GetCode() == KEY_F2) )
3354  {
3355  SC_MOD()->EndReference();
3356  }
3357  else if( mrViewData.GetViewShell()->MoveCursorKeyInput( rKEvt ) )
3358  {
3359  ScRange aRef(
3362  SC_MOD()->SetReference( aRef, mrViewData.GetDocument() );
3363  }
3365  return ;
3366  }
3367  else if( rKeyCode.GetCode() == KEY_RETURN && mrViewData.IsPasteMode()
3368  && SC_MOD()->GetInputOptions().GetEnterPasteMode() )
3369  {
3370  ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
3371  ScClipUtil::PasteFromClipboard( mrViewData, pTabViewShell, true );
3372 
3373  // Clear clipboard content.
3374  uno::Reference<datatransfer::clipboard::XClipboard> xSystemClipboard =
3375  GetClipboard();
3376  if (xSystemClipboard.is())
3377  {
3378  xSystemClipboard->setContents(
3379  uno::Reference<datatransfer::XTransferable>(),
3380  uno::Reference<datatransfer::clipboard::XClipboardOwner>());
3381  }
3382 
3383  // hide the border around the copy source
3385  // Clear CopySourceOverlay in each window of a split/frozen tabview
3387  return;
3388  }
3389  // if semi-modeless SfxChildWindow dialog above, then no KeyInputs:
3390  else if( !mrViewData.IsAnyFillMode() )
3391  {
3392  if (rKeyCode.GetCode() == KEY_ESCAPE)
3393  {
3395  // Clear CopySourceOverlay in each window of a split/frozen tabview
3397  }
3398  // query for existing note marker before calling ViewShell's keyboard handling
3399  // which may remove the marker
3400  bool bHadKeyMarker = mpNoteMarker && mpNoteMarker->IsByKeyboard();
3401  ScTabViewShell* pViewSh = mrViewData.GetViewShell();
3402 
3404  return;
3405 
3406  if (DrawKeyInput(rKEvt, this))
3407  {
3408  const vcl::KeyCode& rLclKeyCode = rKEvt.GetKeyCode();
3409  if (rLclKeyCode.GetCode() == KEY_DOWN
3410  || rLclKeyCode.GetCode() == KEY_UP
3411  || rLclKeyCode.GetCode() == KEY_LEFT
3412  || rLclKeyCode.GetCode() == KEY_RIGHT)
3413  {
3414  ScTabViewShell* pViewShell = mrViewData.GetViewShell();
3415  SfxBindings& rBindings = pViewShell->GetViewFrame()->GetBindings();
3416  rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
3417  rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
3418  }
3419  return;
3420  }
3421 
3422  if (!mrViewData.GetView()->IsDrawSelMode() && !DrawHasMarkedObj()) // No entries in draw mode
3423  {
3424  if (pViewSh->TabKeyInput(rKEvt))
3425  return;
3426  }
3427  else
3428  if (pViewSh->SfxViewShell::KeyInput(rKEvt)) // from SfxViewShell
3429  return;
3430 
3431  vcl::KeyCode aCode = rKEvt.GetKeyCode();
3432  if ( aCode.GetCode() == KEY_ESCAPE && aCode.GetModifier() == 0 )
3433  {
3434  if ( bHadKeyMarker )
3435  HideNoteMarker();
3436  else
3437  pViewSh->Escape();
3438  return;
3439  }
3440  if ( aCode.GetCode() == KEY_F1 && aCode.GetModifier() == KEY_MOD1 )
3441  {
3442  // ctrl-F1 shows or hides the note or redlining info for the cursor position
3443  // (hard-coded because F1 can't be configured)
3444 
3445  if ( bHadKeyMarker )
3446  HideNoteMarker(); // hide when previously visible
3447  else
3449  return;
3450  }
3451  if (aCode.GetCode() == KEY_BRACKETLEFT && aCode.GetModifier() == KEY_MOD1)
3452  {
3453  pViewSh->DetectiveMarkPred();
3454  return;
3455  }
3456  if (aCode.GetCode() == KEY_BRACKETRIGHT && aCode.GetModifier() == KEY_MOD1)
3457  {
3458  pViewSh->DetectiveMarkSucc();
3459  return;
3460  }
3461 
3462  }
3463 
3464  Window::KeyInput(rKEvt);
3465 }
3466 
3468 {
3469  bool bEditView = mrViewData.HasEditView(eWhich);
3470  if (bEditView)
3471  {
3472  ScModule* pScMod = SC_MOD();
3474  if (pHdl)
3475  return pHdl->GetSurroundingText();
3476  }
3477  else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3478  {
3479  // if no cell editview is active, look at drawview
3480  OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3481  if (pOlView && pOlView->GetWindow() == this)
3482  return pOlView->GetSurroundingText();
3483  }
3484 
3485  return Window::GetSurroundingText();
3486 }
3487 
3489 {
3490  bool bEditView = mrViewData.HasEditView(eWhich);
3491  if (bEditView)
3492  {
3493  ScModule* pScMod = SC_MOD();
3495  if (pHdl)
3496  return pHdl->GetSurroundingTextSelection();
3497  }
3498  else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3499  {
3500  // if no cell editview is active, look at drawview
3501  OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3502  if (pOlView && pOlView->GetWindow() == this)
3503  return pOlView->GetSurroundingTextSelection();
3504  }
3505 
3506  return Window::GetSurroundingTextSelection();
3507 }
3508 
3510 {
3511  bool bEditView = mrViewData.HasEditView(eWhich);
3512  if (bEditView)
3513  {
3514  ScModule* pScMod = SC_MOD();
3516  if (pHdl)
3517  return pHdl->DeleteSurroundingText(rSelection);
3518  }
3519  else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
3520  {
3521  // if no cell editview is active, look at drawview
3522  OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
3523  if (pOlView && pOlView->GetWindow() == this)
3524  return pOlView->DeleteSurroundingText(rSelection);
3525  }
3526 
3527  return Window::DeleteSurroundingText(rSelection);
3528 }
3529 
3531 {
3532  DrawEndAction(); // Cancel Select/move on Drawing-Layer
3533 
3534  if (nButtonDown)
3535  {
3538  }
3539 }
3540 
3542 {
3544  InputContextFlags nOptions = bReadOnly ? InputContextFlags::NONE : ( InputContextFlags::Text | InputContextFlags::ExtText );
3545 
3546  // when font from InputContext is used,
3547  // it must be taken from the cursor position's cell attributes
3548 
3549  InputContext aContext;
3550  aContext.SetOptions( nOptions );
3551  SetInputContext( aContext );
3552 }
3553 
3554  // sensitive range (Pixel)
3555 #define SCROLL_SENSITIVE 20
3556 
3557 void ScGridWindow::DropScroll( const Point& rMousePos )
3558 {
3559  ScDocument& rDoc = mrViewData.GetDocument();
3560  SCCOL nDx = 0;
3561  SCROW nDy = 0;
3562  Size aSize = GetOutputSizePixel();
3563 
3564  if (aSize.Width() > SCROLL_SENSITIVE * 3)
3565  {
3566  if ( rMousePos.X() < SCROLL_SENSITIVE && mrViewData.GetPosX(WhichH(eWhich)) > 0 )
3567  nDx = -1;
3568  if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE
3569  && mrViewData.GetPosX(WhichH(eWhich)) < rDoc.MaxCol() )
3570  nDx = 1;
3571  }
3572  if (aSize.Height() > SCROLL_SENSITIVE * 3)
3573  {
3574  if ( rMousePos.Y() < SCROLL_SENSITIVE && mrViewData.GetPosY(WhichV(eWhich)) > 0 )
3575  nDy = -1;
3576  if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE
3577  && mrViewData.GetPosY(WhichV(eWhich)) < rDoc.MaxRow() )
3578  nDy = 1;
3579  }
3580 
3581  if ( nDx != 0 || nDy != 0 )
3582  {
3583  if ( nDx != 0 )
3584  mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
3585  if ( nDy != 0 )
3586  mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
3587  }
3588 }
3589 
3590 static bool lcl_TestScenarioRedliningDrop( const ScDocument* pDoc, const ScRange& aDragRange)
3591 {
3592  // Test, if a scenario is affected by a drop when turing on RedLining,
3593  bool bReturn = false;
3594  SCTAB nTab = aDragRange.aStart.Tab();
3595  SCTAB nTabCount = pDoc->GetTableCount();
3596 
3597  if(pDoc->GetChangeTrack()!=nullptr)
3598  {
3599  if( pDoc->IsScenario(nTab) && pDoc->HasScenarioRange(nTab, aDragRange))
3600  {
3601  bReturn = true;
3602  }
3603  else
3604  {
3605  for(SCTAB i=nTab+1; i<nTabCount && pDoc->IsScenario(i); i++)
3606  {
3607  if(pDoc->HasScenarioRange(i, aDragRange))
3608  {
3609  bReturn = true;
3610  break;
3611  }
3612  }
3613  }
3614  }
3615  return bReturn;
3616 }
3617 
3618 static ScRange lcl_MakeDropRange( const ScDocument& rDoc, SCCOL nPosX, SCROW nPosY, SCTAB nTab, const ScRange& rSource )
3619 {
3620  SCCOL nCol1 = nPosX;
3621  SCCOL nCol2 = nCol1 + ( rSource.aEnd.Col() - rSource.aStart.Col() );
3622  if ( nCol2 > rDoc.MaxCol() )
3623  {
3624  nCol1 -= nCol2 - rDoc.MaxCol();
3625  nCol2 = rDoc.MaxCol();
3626  }
3627  SCROW nRow1 = nPosY;
3628  SCROW nRow2 = nRow1 + ( rSource.aEnd.Row() - rSource.aStart.Row() );
3629  if ( nRow2 > rDoc.MaxRow() )
3630  {
3631  nRow1 -= nRow2 - rDoc.MaxRow();
3632  nRow2 = rDoc.MaxRow();
3633  }
3634 
3635  return ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
3636 }
3637 
3639 {
3640  if ( rEvt.mbLeaving )
3641  {
3642  bDragRect = false;
3644  return rEvt.mnAction;
3645  }
3646 
3647  const ScDragData& rData = SC_MOD()->GetDragData();
3648  if ( rData.pCellTransfer )
3649  {
3650  // Don't move source that would include filtered rows.
3651  if ((rEvt.mnAction & DND_ACTION_MOVE) && rData.pCellTransfer->HasFilteredRows())
3652  {
3653  if (bDragRect)
3654  {
3655  bDragRect = false;
3657  }
3658  return DND_ACTION_NONE;
3659  }
3660 
3661  Point aPos = rEvt.maPosPixel;
3662 
3663  ScDocument* pSourceDoc = rData.pCellTransfer->GetSourceDocument();
3664  ScDocument& rThisDoc = mrViewData.GetDocument();
3665  if (pSourceDoc == &rThisDoc)
3666  {
3667  OUString aName;
3668  if ( rThisDoc.HasChartAtPoint(mrViewData.GetTabNo(), PixelToLogic(aPos), aName ))
3669  {
3670  if (bDragRect) // Remove rectangle
3671  {
3672  bDragRect = false;
3674  }
3675 
3677 
3678  sal_Int8 nRet = rEvt.mnAction;
3679  return nRet;
3680  }
3681  }
3682 
3683  if (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) // whole sheet?
3684  {
3685  bool bOk = rThisDoc.IsDocEditable();
3686  return bOk ? rEvt.mnAction : 0; // don't draw selection frame
3687  }
3688 
3689  SCCOL nPosX;
3690  SCROW nPosY;
3691  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
3692 
3693  ScRange aSourceRange = rData.pCellTransfer->GetRange();
3694  SCCOL nSourceStartX = aSourceRange.aStart.Col();
3695  SCROW nSourceStartY = aSourceRange.aStart.Row();
3696  SCCOL nSourceEndX = aSourceRange.aEnd.Col();
3697  SCROW nSourceEndY = aSourceRange.aEnd.Row();
3698  SCCOL nSizeX = nSourceEndX - nSourceStartX + 1;
3699  SCROW nSizeY = nSourceEndY - nSourceStartY + 1;
3700 
3701  if ( rEvt.mnAction != DND_ACTION_MOVE )
3702  nSizeY = rData.pCellTransfer->GetNonFilteredRows(); // copy/link: no filtered rows
3703 
3704  SCCOL nNewDragX = nPosX - rData.pCellTransfer->GetDragHandleX();
3705  if (nNewDragX<0) nNewDragX=0;
3706  if (nNewDragX+(nSizeX-1) > rThisDoc.MaxCol())
3707  nNewDragX = rThisDoc.MaxCol()-(nSizeX-1);
3708  SCROW nNewDragY = nPosY - rData.pCellTransfer->GetDragHandleY();
3709  if (nNewDragY<0) nNewDragY=0;
3710  if (nNewDragY+(nSizeY-1) > rThisDoc.MaxRow())
3711  nNewDragY = rThisDoc.MaxRow()-(nSizeY-1);
3712 
3713  // don't break scenario ranges, don't drop on filtered
3714  SCTAB nTab = mrViewData.GetTabNo();
3715  ScRange aDropRange = lcl_MakeDropRange( rThisDoc, nNewDragX, nNewDragY, nTab, aSourceRange );
3716  if ( lcl_TestScenarioRedliningDrop( &rThisDoc, aDropRange ) ||
3717  lcl_TestScenarioRedliningDrop( pSourceDoc, aSourceRange ) ||
3718  ScViewUtil::HasFiltered( aDropRange, rThisDoc) )
3719  {
3720  if (bDragRect)
3721  {
3722  bDragRect = false;
3724  }
3725  return DND_ACTION_NONE;
3726  }
3727 
3728  InsCellCmd eDragInsertMode = INS_NONE;
3729  Window::PointerState aState = GetPointerState();
3730 
3731  // check for datapilot item sorting
3732  ScDPObject* pDPObj = nullptr;
3733  if ( &rThisDoc == pSourceDoc && ( pDPObj = rThisDoc.GetDPAtCursor( nNewDragX, nNewDragY, nTab ) ) != nullptr )
3734  {
3735  // drop on DataPilot table: sort or nothing
3736 
3737  bool bDPSort = false;
3738  if ( rThisDoc.GetDPAtCursor( nSourceStartX, nSourceStartY, aSourceRange.aStart.Tab() ) == pDPObj )
3739  {
3740  sheet::DataPilotTableHeaderData aDestData;
3741  pDPObj->GetHeaderPositionData( ScAddress(nNewDragX, nNewDragY, nTab), aDestData );
3742  bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
3743 
3744  // look through the source range
3745  for (SCROW nRow = aSourceRange.aStart.Row(); bValid && nRow <= aSourceRange.aEnd.Row(); ++nRow )
3746  for (SCCOL nCol = aSourceRange.aStart.Col(); bValid && nCol <= aSourceRange.aEnd.Col(); ++nCol )
3747  {
3748  sheet::DataPilotTableHeaderData aSourceData;
3749  pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, aSourceRange.aStart.Tab() ), aSourceData );
3750  if ( aSourceData.Dimension != aDestData.Dimension || aSourceData.MemberName.isEmpty() )
3751  bValid = false; // empty (subtotal) or different field
3752  }
3753 
3754  if ( bValid )
3755  {
3756  bool bIsDataLayout;
3757  OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
3758  const ScDPSaveDimension* pDim = pDPObj->GetSaveData()->GetExistingDimensionByName( aDimName );
3759  if ( pDim )
3760  {
3761  ScRange aOutRange = pDPObj->GetOutRange();
3762 
3763  sheet::DataPilotFieldOrientation nOrient = pDim->GetOrientation();
3764  if ( nOrient == sheet::DataPilotFieldOrientation_COLUMN )
3765  {
3766  eDragInsertMode = INS_CELLSRIGHT;
3767  nSizeY = aOutRange.aEnd.Row() - nNewDragY + 1;
3768  bDPSort = true;
3769  }
3770  else if ( nOrient == sheet::DataPilotFieldOrientation_ROW )
3771  {
3772  eDragInsertMode = INS_CELLSDOWN;
3773  nSizeX = aOutRange.aEnd.Col() - nNewDragX + 1;
3774  bDPSort = true;
3775  }
3776  }
3777  }
3778  }
3779 
3780  if ( !bDPSort )
3781  {
3782  // no valid sorting in a DataPilot table -> disallow
3783  if ( bDragRect )
3784  {
3785  bDragRect = false;
3787  }
3788  return DND_ACTION_NONE;
3789  }
3790  }
3791  else if ( aState.mnState & KEY_MOD2 )
3792  {
3793  if ( &rThisDoc == pSourceDoc && nTab == aSourceRange.aStart.Tab() )
3794  {
3795  tools::Long nDeltaX = std::abs( static_cast< tools::Long >( nNewDragX - nSourceStartX ) );
3796  tools::Long nDeltaY = std::abs( static_cast< tools::Long >( nNewDragY - nSourceStartY ) );
3797  if ( nDeltaX <= nDeltaY )
3798  {
3799  eDragInsertMode = INS_CELLSDOWN;
3800  }
3801  else
3802  {
3803  eDragInsertMode = INS_CELLSRIGHT;
3804  }
3805 
3806  if ( ( eDragInsertMode == INS_CELLSDOWN && nNewDragY <= nSourceEndY &&
3807  ( nNewDragX + nSizeX - 1 ) >= nSourceStartX && nNewDragX <= nSourceEndX &&
3808  ( nNewDragX != nSourceStartX || nNewDragY >= nSourceStartY ) ) ||
3809  ( eDragInsertMode == INS_CELLSRIGHT && nNewDragX <= nSourceEndX &&
3810  ( nNewDragY + nSizeY - 1 ) >= nSourceStartY && nNewDragY <= nSourceEndY &&
3811  ( nNewDragY != nSourceStartY || nNewDragX >= nSourceStartX ) ) )
3812  {
3813  if ( bDragRect )
3814  {
3815  bDragRect = false;
3817  }
3818  return DND_ACTION_NONE;
3819  }
3820  }
3821  else
3822  {
3823  if ( static_cast< tools::Long >( nSizeX ) >= static_cast< tools::Long >( nSizeY ) )
3824  {
3825  eDragInsertMode = INS_CELLSDOWN;
3826 
3827  }
3828  else
3829  {
3830  eDragInsertMode = INS_CELLSRIGHT;
3831  }
3832  }
3833  }
3834 
3835  if ( nNewDragX != nDragStartX || nNewDragY != nDragStartY ||
3836  nDragStartX+nSizeX-1 != nDragEndX || nDragStartY+nSizeY-1 != nDragEndY ||
3837  !bDragRect || eDragInsertMode != meDragInsertMode )
3838  {
3839  nDragStartX = nNewDragX;
3840  nDragStartY = nNewDragY;
3841  nDragEndX = nDragStartX+nSizeX-1;
3842  nDragEndY = nDragStartY+nSizeY-1;
3843  bDragRect = true;
3844  meDragInsertMode = eDragInsertMode;
3845 
3847  }
3848  }
3849 
3850  return rEvt.mnAction;
3851 }
3852 
3854 {
3855  const ScDragData& rData = SC_MOD()->GetDragData();
3856  if ( rEvt.mbLeaving )
3857  {
3858  DrawMarkDropObj( nullptr );
3859  if ( rData.pCellTransfer )
3860  return AcceptPrivateDrop( rEvt ); // hide drop marker for internal D&D
3861  else
3862  return rEvt.mnAction;
3863  }
3864 
3865  if ( mrViewData.GetDocShell()->IsReadOnly() )
3866  return DND_ACTION_NONE;
3867 
3868  ScDocument& rThisDoc = mrViewData.GetDocument();
3869  sal_Int8 nRet = DND_ACTION_NONE;
3870 
3871  if (rData.pCellTransfer)
3872  {
3873  ScRange aSource = rData.pCellTransfer->GetRange();
3874  if ( aSource.aStart.Col() != 0 || aSource.aEnd.Col() != rThisDoc.MaxCol() ||
3875  aSource.aStart.Row() != 0 || aSource.aEnd.Row() != rThisDoc.MaxRow() )
3876  DropScroll( rEvt.maPosPixel );
3877 
3878  nRet = AcceptPrivateDrop( rEvt );
3879  }
3880  else
3881  {
3882  if ( !rData.aLinkDoc.isEmpty() )
3883  {
3884  OUString aThisName;
3885  ScDocShell* pDocSh = mrViewData.GetDocShell();
3886  if (pDocSh && pDocSh->HasName())
3887  aThisName = pDocSh->GetMedium()->GetName();
3888 
3889  if ( rData.aLinkDoc != aThisName )
3890  nRet = rEvt.mnAction;
3891  }
3892  else if (!rData.aJumpTarget.isEmpty())
3893  {
3894  // internal bookmarks (from Navigator)
3895  // local jumps from an unnamed document are possible only within a document
3896 
3897  if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
3898  nRet = rEvt.mnAction;
3899  }
3900  else
3901  {
3902  sal_Int8 nMyAction = rEvt.mnAction;
3903 
3904  // clear DND_ACTION_LINK when other actions are set. The usage below cannot handle
3905  // multiple set values
3906  if((nMyAction & DND_ACTION_LINK) && (nMyAction & DND_ACTION_COPYMOVE))
3907  {
3908  nMyAction &= ~DND_ACTION_LINK;
3909  }
3910 
3911  if ( !rData.pDrawTransfer ||
3912  !IsMyModel(rData.pDrawTransfer->GetDragSourceView()) ) // drawing within the document
3913  if ( rEvt.mbDefault && nMyAction == DND_ACTION_MOVE )
3914  nMyAction = DND_ACTION_COPY;
3915 
3916  SdrObject* pHitObj = rThisDoc.GetObjectAtPoint(
3918  if ( pHitObj && nMyAction == DND_ACTION_LINK )
3919  {
3920  if ( IsDropFormatSupported(SotClipboardFormatId::SVXB)
3921  || IsDropFormatSupported(SotClipboardFormatId::GDIMETAFILE)
3922  || IsDropFormatSupported(SotClipboardFormatId::PNG)
3923  || IsDropFormatSupported(SotClipboardFormatId::BITMAP) )
3924  {
3925  // graphic dragged onto drawing object
3926  DrawMarkDropObj( pHitObj );
3927  nRet = nMyAction;
3928  }
3929  }
3930  if (!nRet)
3931  {
3932  DrawMarkDropObj(nullptr);
3933 
3934  switch ( nMyAction )
3935  {
3936  case DND_ACTION_COPY:
3937  case DND_ACTION_MOVE:
3938  case DND_ACTION_COPYMOVE:
3939  {
3940  bool bMove = ( nMyAction == DND_ACTION_MOVE );
3941  if ( IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE ) ||
3942  IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
3943  IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
3944  IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
3945  IsDropFormatSupported( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
3946  IsDropFormatSupported( SotClipboardFormatId::STRING ) ||
3947  IsDropFormatSupported( SotClipboardFormatId::STRING_TSVC ) ||
3948  IsDropFormatSupported( SotClipboardFormatId::SYLK ) ||
3949  IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
3950  IsDropFormatSupported( SotClipboardFormatId::HTML ) ||
3951  IsDropFormatSupported( SotClipboardFormatId::HTML_SIMPLE ) ||
3952  IsDropFormatSupported( SotClipboardFormatId::DIF ) ||
3953  IsDropFormatSupported( SotClipboardFormatId::DRAWING ) ||
3954  IsDropFormatSupported( SotClipboardFormatId::SVXB ) ||
3955  IsDropFormatSupported( SotClipboardFormatId::RTF ) ||
3956  IsDropFormatSupported( SotClipboardFormatId::RICHTEXT ) ||
3957  IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ) ||
3958  IsDropFormatSupported( SotClipboardFormatId::PNG ) ||
3959  IsDropFormatSupported( SotClipboardFormatId::BITMAP ) ||
3960  IsDropFormatSupported( SotClipboardFormatId::SBA_DATAEXCHANGE ) ||
3961  IsDropFormatSupported( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) ||
3962  ( !bMove && (
3963  IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
3964  IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
3965  IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
3966  IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
3967  IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
3968  IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) ) ) )
3969  {
3970  nRet = nMyAction;
3971  }
3972  }
3973  break;
3974  case DND_ACTION_LINK:
3975  if ( IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
3976  IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
3977  IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
3978  IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
3979  IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
3980  IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
3981  IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
3982  IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
3983  IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
3984  {
3985  nRet = nMyAction;
3986  }
3987  break;
3988  }
3989 
3990  if ( nRet )
3991  {
3992  // Simple check for protection: It's not known here if the drop will result
3993  // in cells or drawing objects (some formats can be both) and how many cells
3994  // the result will be. But if IsFormatEditable for the drop cell position
3995  // is sal_False (ignores matrix formulas), nothing can be pasted, so the drop
3996  // can already be rejected here.
3997 
3998  Point aPos = rEvt.maPosPixel;
3999  SCCOL nPosX;
4000  SCROW nPosY;
4001  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4002  SCTAB nTab = mrViewData.GetTabNo();
4003  ScDocument& rDoc = mrViewData.GetDocument();
4004 
4005  ScEditableTester aTester( rDoc, nTab, nPosX,nPosY, nPosX,nPosY );
4006  if ( !aTester.IsFormatEditable() )
4007  nRet = DND_ACTION_NONE; // forbidden
4008  }
4009  }
4010  }
4011 
4012  // scroll only for accepted formats
4013  if (nRet)
4014  DropScroll( rEvt.maPosPixel );
4015  }
4016 
4017  return nRet;
4018 }
4019 
4020 static SotClipboardFormatId lcl_GetDropFormatId( const uno::Reference<datatransfer::XTransferable>& xTransfer, bool bPreferText )
4021 {
4022  TransferableDataHelper aDataHelper( xTransfer );
4023 
4024  if ( !aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
4025  {
4026  // use bookmark formats if no sba is present
4027 
4028  if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
4029  return SotClipboardFormatId::SOLK;
4030  else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
4031  return SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
4032  else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
4033  return SotClipboardFormatId::NETSCAPE_BOOKMARK;
4034  else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4035  return SotClipboardFormatId::FILEGRPDESCRIPTOR;
4036  }
4037 
4038  SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
4039  if ( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) )
4040  nFormatId = SotClipboardFormatId::DRAWING;
4041  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
4042  nFormatId = SotClipboardFormatId::SVXB;
4043  else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) )
4044  {
4045  // If it's a Writer object, insert RTF instead of OLE
4046 
4047  bool bDoRtf = false;
4050  if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) &&
4051  aDataHelper.GetSotStorageStream( SotClipboardFormatId::EMBED_SOURCE, xStm ) )
4052  {
4053  bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
4054  aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
4055  && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
4056  }
4057  if ( bDoRtf )
4058  nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
4059  else
4060  nFormatId = SotClipboardFormatId::EMBED_SOURCE;
4061  }
4062  else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
4063  nFormatId = SotClipboardFormatId::LINK_SOURCE;
4064  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
4065  nFormatId = SotClipboardFormatId::SBA_DATAEXCHANGE;
4066  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) )
4067  nFormatId = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
4068  else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_8 ) )
4069  nFormatId = SotClipboardFormatId::BIFF_8;
4070  else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_5 ) )
4071  nFormatId = SotClipboardFormatId::BIFF_5;
4072  else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) )
4073  nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
4074  else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) )
4075  nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
4076  else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
4077  nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
4078  else if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
4079  nFormatId = SotClipboardFormatId::RTF;
4080  else if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
4081  nFormatId = SotClipboardFormatId::RICHTEXT;
4082  else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML ) )
4083  nFormatId = SotClipboardFormatId::HTML;
4084  else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) )
4085  nFormatId = SotClipboardFormatId::HTML_SIMPLE;
4086  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SYLK ) )
4087  nFormatId = SotClipboardFormatId::SYLK;
4088  else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
4089  nFormatId = SotClipboardFormatId::LINK;
4090  else if ( bPreferText && aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) // #i86734# the behaviour introduced in #i62773# is wrong when pasting
4091  nFormatId = SotClipboardFormatId::STRING;
4092  else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
4093  nFormatId = SotClipboardFormatId::FILE_LIST;
4094  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) ) // #i62773# FILE_LIST/FILE before STRING (Unix file managers)
4095  nFormatId = SotClipboardFormatId::SIMPLE_FILE;
4096  else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING_TSVC ) )
4097  nFormatId = SotClipboardFormatId::STRING_TSVC;
4098  else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
4099  nFormatId = SotClipboardFormatId::STRING;
4100  else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
4101  nFormatId = SotClipboardFormatId::GDIMETAFILE;
4102  else if ( aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
4103  nFormatId = SotClipboardFormatId::PNG;
4104  else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) )
4105  nFormatId = SotClipboardFormatId::BITMAP;
4106 
4107  return nFormatId;
4108 }
4109 
4110 static SotClipboardFormatId lcl_GetDropLinkId( const uno::Reference<datatransfer::XTransferable>& xTransfer )
4111 {
4112  TransferableDataHelper aDataHelper( xTransfer );
4113 
4114  SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
4115  if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
4116  nFormatId = SotClipboardFormatId::LINK_SOURCE;
4117  else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
4118  nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
4119  else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
4120  nFormatId = SotClipboardFormatId::LINK;
4121  else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
4122  nFormatId = SotClipboardFormatId::FILE_LIST;
4123  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )
4124  nFormatId = SotClipboardFormatId::SIMPLE_FILE;
4125  else if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
4126  nFormatId = SotClipboardFormatId::SOLK;
4127  else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
4128  nFormatId = SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
4129  else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
4130  nFormatId = SotClipboardFormatId::NETSCAPE_BOOKMARK;
4131  else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
4132  nFormatId = SotClipboardFormatId::FILEGRPDESCRIPTOR;
4133 
4134  return nFormatId;
4135 }
4136 
4138 {
4139  // hide drop marker
4140  bDragRect = false;
4142 
4143  ScModule* pScMod = SC_MOD();
4144  const ScDragData& rData = pScMod->GetDragData();
4145 
4147  PixelToLogic(rEvt.maPosPixel), rEvt.mnAction );
4148 }
4149 
4151  const Point& rLogicPos, sal_Int8 nDndAction )
4152 {
4153  if ( !pTransObj )
4154  return 0;
4155 
4156  ScDocument* pSourceDoc = pTransObj->GetSourceDocument();
4157  ScDocShell* pDocSh = mrViewData.GetDocShell();
4158  ScDocument& rThisDoc = mrViewData.GetDocument();
4159  ScViewFunc* pView = mrViewData.GetView();
4160  SCTAB nThisTab = mrViewData.GetTabNo();
4161  ScDragSrc nFlags = pTransObj->GetDragSourceFlags();
4162 
4163  bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
4164  bool bIsMove = ( nDndAction == DND_ACTION_MOVE && !bIsNavi );
4165 
4166  // workaround for wrong nDndAction on Windows when pressing solely
4167  // the Alt key during drag and drop;
4168  // can be removed after #i79215# has been fixed
4169  if ( meDragInsertMode != INS_NONE )
4170  {
4171  bIsMove = ( nDndAction & DND_ACTION_MOVE && !bIsNavi );
4172  }
4173 
4174  bool bIsLink = ( nDndAction == DND_ACTION_LINK );
4175 
4176  ScRange aSource = pTransObj->GetRange();
4177 
4178  // only use visible tab from source range - when dragging within one table,
4179  // all selected tables at the time of dropping are used (handled in MoveBlockTo)
4180  SCTAB nSourceTab = pTransObj->GetVisibleTab();
4181  aSource.aStart.SetTab( nSourceTab );
4182  aSource.aEnd.SetTab( nSourceTab );
4183 
4184  SCCOL nSizeX = aSource.aEnd.Col() - aSource.aStart.Col() + 1;
4185  SCROW nSizeY = (bIsMove ? (aSource.aEnd.Row() - aSource.aStart.Row() + 1) :
4186  pTransObj->GetNonFilteredRows()); // copy/link: no filtered rows
4187  ScRange aDest( nDestPosX, nDestPosY, nThisTab,
4188  nDestPosX + nSizeX - 1, nDestPosY + nSizeY - 1, nThisTab );
4189 
4190  /* NOTE: AcceptPrivateDrop() already checked for filtered conditions during
4191  * dragging and adapted drawing of the selection frame. We check here
4192  * (again) because this may actually also be called from PasteSelection(),
4193  * we would have to duplicate determination of flags and destination range
4194  * and would lose the context of the "filtered destination is OK" cases
4195  * below, which is already awkward enough as is. */
4196 
4197  // Don't move filtered source.
4198  bool bFiltered = (bIsMove && pTransObj->HasFilteredRows());
4199  if (!bFiltered)
4200  {
4201  if (pSourceDoc != &rThisDoc && ((nFlags & ScDragSrc::Table) ||
4202  (!bIsLink && meDragInsertMode == INS_NONE)))
4203  {
4204  // Nothing. Either entire sheet to be dropped, or the one case
4205  // where PasteFromClip() is to be called that handles a filtered
4206  // destination itself. Drag-copy from another document without
4207  // inserting cells.
4208  }
4209  else
4210  // Don't copy or move to filtered destination.
4211  bFiltered = ScViewUtil::HasFiltered(aDest, rThisDoc);
4212  }
4213 
4214  bool bDone = false;
4215 
4216  if (!bFiltered && pSourceDoc == &rThisDoc)
4217  {
4218  if (nFlags & ScDragSrc::Table) // whole sheet?
4219  {
4220  if ( rThisDoc.IsDocEditable() )
4221  {
4222  SCTAB nSrcTab = aSource.aStart.Tab();
4223  mrViewData.GetDocShell()->MoveTable( nSrcTab, nThisTab, !bIsMove, true ); // with Undo
4224  pView->SetTabNo( nThisTab, true );
4225  bDone = true;
4226  }
4227  }
4228  else // move/copy block
4229  {
4230  OUString aChartName;
4231  if (rThisDoc.HasChartAtPoint( nThisTab, rLogicPos, aChartName ))
4232  {
4233  OUString aRangeName(aSource.Format(rThisDoc, ScRefFlags::RANGE_ABS_3D,
4234  rThisDoc.GetAddressConvention()));
4235  SfxStringItem aNameItem( SID_CHART_NAME, aChartName );
4236  SfxStringItem aRangeItem( SID_CHART_SOURCE, aRangeName );
4237  sal_uInt16 nId = bIsMove ? SID_CHART_SOURCE : SID_CHART_ADDSOURCE;
4239  SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
4240  { &aRangeItem, &aNameItem });
4241  bDone = true;
4242  }
4243  else if ( rThisDoc.GetDPAtCursor( nDestPosX, nDestPosY, nThisTab ) )
4244  {
4245  // drop on DataPilot table: try to sort, fail if that isn't possible
4246 
4247  ScAddress aDestPos( nDestPosX, nDestPosY, nThisTab );
4248  if ( aDestPos != aSource.aStart )
4249  bDone = mrViewData.GetView()->DataPilotMove( aSource, aDestPos );
4250  else
4251  bDone = true; // same position: nothing
4252  }
4253  else if ( nDestPosX != aSource.aStart.Col() || nDestPosY != aSource.aStart.Row() ||
4254  nSourceTab != nThisTab )
4255  {
4256  OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
4257  pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4258 
4259  SCCOL nCorrectCursorPosCol = 0;
4260  SCROW nCorrectCursorPosRow = 0;
4261 
4262  bDone = true;
4263  if ( meDragInsertMode != INS_NONE )
4264  {
4265  // call with bApi = sal_True to avoid error messages in drop handler
4266  bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4267  if ( bDone )
4268  {
4269  if ( nThisTab == nSourceTab )
4270  {
4271  if ( meDragInsertMode == INS_CELLSDOWN &&
4272  nDestPosX == aSource.aStart.Col() && nDestPosY < aSource.aStart.Row() )
4273  {
4274  ScRange aErrorRange( ScAddress::UNINITIALIZED );
4275  bDone = aSource.Move( 0, nSizeY, 0, aErrorRange, pSourceDoc );
4276  nCorrectCursorPosRow = nSizeY;
4277  }
4278  else if ( meDragInsertMode == INS_CELLSRIGHT &&
4279  nDestPosY == aSource.aStart.Row() && nDestPosX < aSource.aStart.Col() )
4280  {
4281  ScRange aErrorRange( ScAddress::UNINITIALIZED );
4282  bDone = aSource.Move( nSizeX, 0, 0, aErrorRange, pSourceDoc );
4283  nCorrectCursorPosCol = nSizeX;
4284  }
4285  }
4286  pDocSh->UpdateOle(mrViewData);
4287  pView->CellContentChanged();
4288  }
4289  }
4290 
4291  if ( bDone )
4292  {
4293  if ( bIsLink )
4294  {
4295  bDone = pView->LinkBlock( aSource, aDest.aStart );
4296  }
4297  else
4298  {
4299  bDone = pView->MoveBlockTo( aSource, aDest.aStart, bIsMove );
4300  }
4301  }
4302 
4303  if ( bDone && meDragInsertMode != INS_NONE && bIsMove && nThisTab == nSourceTab )
4304  {
4307  {
4308  eCmd = DelCellCmd::CellsUp;
4309  }
4310  else if ( meDragInsertMode == INS_CELLSRIGHT )
4311  {
4312  eCmd = DelCellCmd::CellsLeft;
4313  }
4314 
4315  if ( ( eCmd == DelCellCmd::CellsUp && nDestPosX == aSource.aStart.Col() ) ||
4316  ( eCmd == DelCellCmd::CellsLeft && nDestPosY == aSource.aStart.Row() ) )
4317  {
4318  // call with bApi = sal_True to avoid error messages in drop handler
4319  bDone = pDocSh->GetDocFunc().DeleteCells( aSource, nullptr, eCmd, true /*bApi*/ );
4320  if ( bDone )
4321  {
4322  if ( eCmd == DelCellCmd::CellsUp && nDestPosY > aSource.aEnd.Row() )
4323  {
4324  ScRange aErrorRange( ScAddress::UNINITIALIZED );
4325  bDone = aDest.Move( 0, -nSizeY, 0, aErrorRange, &rThisDoc );
4326  }
4327  else if ( eCmd == DelCellCmd::CellsLeft && nDestPosX > aSource.aEnd.Col() )
4328  {
4329  ScRange aErrorRange( ScAddress::UNINITIALIZED );
4330  bDone = aDest.Move( -nSizeX, 0, 0, aErrorRange, &rThisDoc );
4331  }
4332  pDocSh->UpdateOle(mrViewData);
4333  pView->CellContentChanged();
4334  }
4335  }
4336  }
4337 
4338  if ( bDone )
4339  {
4340  pView->MarkRange( aDest, false );
4341 
4342  SCCOL nDCol;
4343  SCROW nDRow;
4344  if (pTransObj->WasSourceCursorInSelection())
4345  {
4346  nDCol = pTransObj->GetSourceCursorX() - aSource.aStart.Col() + nCorrectCursorPosCol;
4347  nDRow = pTransObj->GetSourceCursorY() - aSource.aStart.Row() + nCorrectCursorPosRow;
4348  }
4349  else
4350  {
4351  nDCol = 0;
4352  nDRow = 0;
4353  }
4354  pView->SetCursor( aDest.aStart.Col() + nDCol, aDest.aStart.Row() + nDRow );
4355  }
4356 
4357  pDocSh->GetUndoManager()->LeaveListAction();
4358 
4359  }
4360  else
4361  bDone = true; // nothing to do
4362  }
4363 
4364  if (bDone)
4365  pTransObj->SetDragWasInternal(); // don't delete source in DragFinished
4366  }
4367  else if ( !bFiltered && pSourceDoc ) // between documents
4368  {
4369  if (nFlags & ScDragSrc::Table) // copy/link sheets between documents
4370  {
4371  if ( rThisDoc.IsDocEditable() )
4372  {
4373  ScDocShell* pSrcShell = pTransObj->GetSourceDocShell();
4374 
4375  std::vector<SCTAB> nTabs;
4376 
4377  ScMarkData aMark = pTransObj->GetSourceMarkData();
4378  SCTAB nTabCount = pSourceDoc->GetTableCount();
4379 
4380  for(SCTAB i=0; i<nTabCount; i++)
4381  {
4382  if(aMark.GetTableSelect(i))
4383  {
4384  nTabs.push_back(i);
4385  for(SCTAB j=i+1;j<nTabCount;j++)
4386  {
4387  if((!pSourceDoc->IsVisible(j))&&(pSourceDoc->IsScenario(j)))
4388  {
4389  nTabs.push_back( j );
4390  i=j;
4391  }
4392  else break;
4393  }
4394  }
4395  }
4396 
4397  pView->ImportTables( pSrcShell,static_cast<SCTAB>(nTabs.size()), nTabs.data(), bIsLink, nThisTab );
4398  bDone = true;
4399  }
4400  }
4401  else if ( bIsLink )
4402  {
4403  // as in PasteDDE
4404  // (external references might be used instead?)
4405 
4406  SfxObjectShell* pSourceSh = pSourceDoc->GetDocumentShell();
4407  OSL_ENSURE(pSourceSh, "drag document has no shell");
4408  if (pSourceSh)
4409  {
4410  OUString aUndo = ScResId( STR_UNDO_COPY );
4411  pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4412 
4413  bDone = true;
4414  if ( meDragInsertMode != INS_NONE )
4415  {
4416  // call with bApi = sal_True to avoid error messages in drop handler
4417  bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4418  if ( bDone )
4419  {
4420  pDocSh->UpdateOle(mrViewData);
4421  pView->CellContentChanged();
4422  }
4423  }
4424 
4425  if ( bDone )
4426  {
4427  OUString aApp = Application::GetAppName();
4428  OUString aTopic = pSourceSh->GetTitle( SFX_TITLE_FULLNAME );
4429  OUString aItem(aSource.Format(*pSourceDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
4430 
4431  // TODO: we could define ocQuote for "
4432  const OUString aQuote('"');
4433  const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
4434  OUString aFormula =
4435  "=" +
4438  aQuote +
4439  aApp +
4440  aQuote +
4441  sSep +
4442  aQuote +
4443  aTopic +
4444  aQuote +
4445  sSep +
4446  aQuote +
4447  aItem +
4448  aQuote +
4450 
4451  pView->DoneBlockMode();
4452  pView->InitBlockMode( nDestPosX, nDestPosY, nThisTab );
4453  pView->MarkCursor( nDestPosX + nSizeX - 1,
4454  nDestPosY + nSizeY - 1, nThisTab );
4455 
4456  pView->EnterMatrix( aFormula, ::formula::FormulaGrammar::GRAM_NATIVE );
4457 
4458  pView->MarkRange( aDest, false );
4459  pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
4460  }
4461 
4462  pDocSh->GetUndoManager()->LeaveListAction();
4463  }
4464  }
4465  else
4466  {
4469 
4470  OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
4471  pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
4472 
4473  bDone = true;
4474  if ( meDragInsertMode != INS_NONE )
4475  {
4476  // call with bApi = sal_True to avoid error messages in drop handler
4477  bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
4478  if ( bDone )
4479  {
4480  pDocSh->UpdateOle(mrViewData);
4481  pView->CellContentChanged();
4482  }
4483  }
4484 
4485  if ( bDone )
4486  {
4487  pView->Unmark(); // before SetCursor, so CheckSelectionTransfer isn't called with a selection
4488  pView->SetCursor( nDestPosX, nDestPosY );
4489  bDone = pView->PasteFromClip( InsertDeleteFlags::ALL, pTransObj->GetDocument() ); // clip-doc
4490  if ( bDone )
4491  {
4492  pView->MarkRange( aDest, false );
4493  pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
4494  }
4495  }
4496 
4497  pDocSh->GetUndoManager()->LeaveListAction();
4498 
4499  // no longer call ResetMark here - the inserted block has been selected
4500  // and may have been copied to primary selection
4501  }
4502  }
4503 
4504  sal_Int8 nRet = bDone ? nDndAction : DND_ACTION_NONE;
4505  return nRet;
4506 }
4507 
4509 {
4510  DrawMarkDropObj( nullptr ); // drawing layer
4511 
4512  ScModule* pScMod = SC_MOD();
4513  const ScDragData& rData = pScMod->GetDragData();
4514  if (rData.pCellTransfer)
4515  return ExecutePrivateDrop( rEvt );
4516 
4517  Point aPos = rEvt.maPosPixel;
4518 
4519  if ( !rData.aLinkDoc.isEmpty() )
4520  {
4521  // try to insert a link
4522 
4523  bool bOk = true;
4524  OUString aThisName;
4525  ScDocShell* pDocSh = mrViewData.GetDocShell();
4526  if (pDocSh && pDocSh->HasName())
4527  aThisName = pDocSh->GetMedium()->GetName();
4528 
4529  if ( rData.aLinkDoc == aThisName ) // error - no link within a document
4530  bOk = false;
4531  else
4532  {
4533  ScViewFunc* pView = mrViewData.GetView();
4534  if ( !rData.aLinkTable.isEmpty() )
4536  rData.aLinkTable );
4537  else if ( !rData.aLinkArea.isEmpty() )
4538  {
4539  SCCOL nPosX;
4540  SCROW nPosY;
4541  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4542  pView->MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
4543 
4545  rData.aLinkArea );
4546  }
4547  else
4548  {
4549  OSL_FAIL("drop with link: no sheet nor area");
4550  bOk = false;
4551  }
4552  }
4553 
4554  return bOk ? rEvt.mnAction : DND_ACTION_NONE; // don't try anything else
4555  }
4556 
4557  Point aLogicPos = PixelToLogic(aPos);
4558  bool bIsLink = ( rEvt.mnAction == DND_ACTION_LINK );
4559 
4560  if (!bIsLink && rData.pDrawTransfer)
4561  {
4562  ScDragSrc nFlags = rData.pDrawTransfer->GetDragSourceFlags();
4563 
4564  bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
4565  bool bIsMove = ( rEvt.mnAction == DND_ACTION_MOVE && !bIsNavi );
4566 
4567  bPasteIsMove = bIsMove;
4568 
4570  aLogicPos, rData.pDrawTransfer->GetModel(), false, u"A", u"B");
4571 
4572  if (bPasteIsMove)
4574  bPasteIsMove = false;
4575 
4576  return rEvt.mnAction;
4577  }
4578 
4579  SCCOL nPosX;
4580  SCROW nPosY;
4581  mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
4582 
4583  if (!rData.aJumpTarget.isEmpty())
4584  {
4585  // internal bookmark (from Navigator)
4586  // bookmark clipboard formats are in PasteScDataObject
4587 
4588  if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
4589  {
4591  nPosX, nPosY );
4592  return rEvt.mnAction;
4593  }
4594  }
4595 
4596  ScDocument& rThisDoc = mrViewData.GetDocument();
4597  SdrObject* pHitObj = rThisDoc.GetObjectAtPoint( mrViewData.GetTabNo(), PixelToLogic(aPos) );
4598  if ( pHitObj && bIsLink )
4599  {
4600  // dropped on drawing object
4601  // PasteOnDrawObjectLinked checks for valid formats
4602  if ( mrViewData.GetView()->PasteOnDrawObjectLinked( rEvt.maDropEvent.Transferable, *pHitObj ) )
4603  return rEvt.mnAction;
4604  }
4605 
4606  bool bDone = false;
4607 
4608  SotClipboardFormatId nFormatId = bIsLink ?
4609  lcl_GetDropLinkId( rEvt.maDropEvent.Transferable ) :
4610  lcl_GetDropFormatId( rEvt.maDropEvent.Transferable, false );
4611  if ( nFormatId != SotClipboardFormatId::NONE )
4612  {
4613  pScMod->SetInExecuteDrop( true ); // #i28468# prevent error messages from PasteDataFormat
4614  bDone = mrViewData.GetView()->PasteDataFormat(
4615  nFormatId, rEvt.maDropEvent.Transferable, nPosX, nPosY, &aLogicPos, bIsLink );
4616  pScMod->SetInExecuteDrop( false );
4617  }
4618 
4619  sal_Int8 nRet = bDone ? rEvt.mnAction : DND_ACTION_NONE;
4620  return nRet;
4621 }
4622 
4623 void ScGridWindow::PasteSelection( const Point& rPosPixel )
4624 {
4625  Point aLogicPos = PixelToLogic( rPosPixel );
4626 
4627  SCCOL nPosX;
4628  SCROW nPosY;
4629  mrViewData.GetPosFromPixel( rPosPixel.X(), rPosPixel.Y(), eWhich, nPosX, nPosY );
4630 
4631  // If the mouse down was inside a visible note window, ignore it and
4632  // leave it up to the ScPostIt to handle it
4633  SdrView* pDrawView = mrViewData.GetViewShell()->GetScDrawView();
4634  if (pDrawView)
4635  {
4636  const size_t nCount = pDrawView->GetMarkedObjectCount();
4637  for (size_t i = 0; i < nCount; ++i)
4638  {
4639  SdrObject* pObj = pDrawView->GetMarkedObjectByIndex(i);
4640  if (pObj && pObj->GetLogicRect().Contains(aLogicPos))
4641  {
4642  // Inside an active drawing object. Bail out.
4643  return;
4644  }
4645  }
4646  }
4647 
4648  ScSelectionTransferObj* pOwnSelection = SC_MOD()->GetSelectionTransfer();
4649  if ( pOwnSelection )
4650  {
4651  // within Calc
4652 
4653  // keep a reference to the data in case the selection is changed during paste
4654  rtl::Reference<ScTransferObj> pCellTransfer = pOwnSelection->GetCellData();
4655  if ( pCellTransfer )
4656  {
4657  DropTransferObj( pCellTransfer.get(), nPosX, nPosY, aLogicPos, DND_ACTION_COPY );
4658  }
4659  else
4660  {
4661  // keep a reference to the data in case the selection is changed during paste
4662  rtl::Reference<ScDrawTransferObj> pDrawTransfer = pOwnSelection->GetDrawData();
4663  if ( pDrawTransfer )
4664  {
4665  // bSameDocClipboard argument for PasteDraw is needed
4666  // because only DragData is checked directly inside PasteDraw
4668  aLogicPos, pDrawTransfer->GetModel(), false,
4669  pDrawTransfer->GetShellID(), SfxObjectShell::CreateShellID(mrViewData.GetDocShell()));
4670  }
4671  }
4672  }
4673  else
4674  {
4675  // get selection from system
4677  const uno::Reference<datatransfer::XTransferable>& xTransferable = aDataHelper.GetTransferable();
4678  if ( xTransferable.is() )
4679  {
4680  SotClipboardFormatId nFormatId = lcl_GetDropFormatId( xTransferable, true );
4681  if ( nFormatId != SotClipboardFormatId::NONE )
4682  mrViewData.GetView()->PasteDataFormat( nFormatId, xTransferable, nPosX, nPosY, &aLogicPos );
4683  }
4684  }
4685 }
4686 
4688 {
4689  if (!mrViewData.HasEditView(eWhich))
4690  return;
4691 
4692  EditView* pView;
4693  SCCOL nCol;
4694  SCROW nRow;
4695  mrViewData.GetEditView( eWhich, pView, nCol, nRow );
4696  SCCOL nEndCol = mrViewData.GetEditEndCol();
4697  SCROW nEndRow = mrViewData.GetEditEndRow();
4698 
4699  // hide EditView?
4700 
4701  bool bHide = ( nEndCol<mrViewData.GetPosX(eHWhich) || nEndRow<mrViewData.GetPosY(eVWhich) );
4702  if ( SC_MOD()->IsFormulaMode() )
4704  bHide = true;
4705 
4706  if (bHide)
4707  {
4708  tools::Rectangle aRect = pView->GetOutputArea();
4709  tools::Long nHeight = aRect.Bottom() - aRect.Top();
4711  Height() * 2 );
4712  aRect.SetBottom( aRect.Top() + nHeight );
4713  pView->SetOutputArea( aRect );
4714  pView->HideCursor();
4715  }
4716  else
4717  {
4718  // bForceToTop = sal_True for editing
4719  tools::Rectangle aPixRect = mrViewData.GetEditArea( eWhich, nCol, nRow, this, nullptr, true );
4720 
4723  comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
4724  {