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