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