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