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