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