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