/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
//tdf#133971 hold self-ref until we return auto xThis(shared_from_this());
pGridWin->FilterSelect(nSel); if (xThis.use_count() == 1)
{ // tdf#133855 we got disposed by FilterSelect return;
}
pGridWin->ClickExtern();
}
staticbool lcl_IsEditableMatrix( ScDocument& rDoc, const ScRange& rRange )
{ // If it is an editable range and if there is a Matrix cell at the bottom right with an // origin top left then the range will be set to contain the exact matrix. //! Extract the MatrixEdges functions directly from the column ??? if ( !rDoc.IsBlockEditable( rRange.aStart.Tab(), rRange.aStart.Col(),rRange.aStart.Row(),
rRange.aEnd.Col(),rRange.aEnd.Row() ) ) returnfalse;
if (mpSpellCheckCxt)
mpSpellCheckCxt->reset();
mpSpellCheckCxt.reset();
vcl::Window::dispose();
}
void ScGridWindow::ClickExtern()
{ do
{ // #i84277# when initializing the filter box, a Basic error can deactivate the view if (mpFilterBox && mpFilterBox->IsInInit()) break;
mpFilterBox.reset();
} while (false);
if (mpDPFieldPopup)
{
mpDPFieldPopup->close(false);
mpDPFieldPopup.reset();
}
}
IMPL_LINK_NOARG(ScGridWindow, PopupModeEndHdl, weld::Popover&, void)
{ if (mpFilterBox)
{ bool bMouseWasCaptured = mpFilterBox->MouseWasCaptured();
mpFilterBox->SetCancelled(); // cancel select // restore the mouse capture state of the GridWindow to // what it was at initial popup time
SAL_WARN_IF(bMouseWasCaptured, "sc.ui", "Is there a scenario where the mouse was captured before mouse down?"); if (bMouseWasCaptured)
CaptureMouse();
}
GrabFocus();
}
IMPL_LINK( ScGridWindow, PopupSpellingHdl, SpellCallbackInfo&, rInfo, void )
{ if( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
mrViewData.GetDispatcher().Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON ); elseif (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS)
mrViewData.GetDispatcher().Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON ); else//IGNOREWORD, ADDTODICTIONARY, WORDLANGUAGE, PARALANGUAGE
{ // The spelling status of the word has changed. Close the cell to reset the caches
ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell()); if (pHdl)
pHdl->EnterHandler();
}
}
class AutoFilterAction : public ScCheckListMenuControl::Action
{ protected:
VclPtr<ScGridWindow> mpWindow;
ScGridWindow::AutoFilterMode meMode; public:
AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
mpWindow(p), meMode(eMode) {} virtualbool execute() override
{
mpWindow->UpdateAutoFilterFromMenu(meMode); // UpdateAutoFilterFromMenu manually closes the popup so return // false to not attempt a second close returnfalse;
}
};
class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
{
VclPtr<ScGridWindow> mpWindow;
ScAddress maPos; public:
AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
mpWindow(p), maPos(rPos) {} virtualbool execute() override
{
mpWindow->RefreshAutoFilterButton(maPos);
mpWindow->GrabFocus(); returnfalse; // this is called after the popup has been closed
}
};
class AutoFilterSubMenuAction : public AutoFilterAction
{ protected:
ScListSubMenuControl* m_pSubMenu;
// Disable color filter when active color was selected if (bActive)
{
aParam.RemoveAllEntriesByField(rPos.Col());
pEntry = nullptr; // invalidated by RemoveAllEntriesByField call
// Estimate the width (in pixels) of the longest text in the list
ScFilterEntries aFilterEntries;
rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);
int nMaxTextWidth = 0; if (aFilterEntries.size() <= 10)
{ // do pixel calculation for all elements of short lists for (constauto& rEntry : aFilterEntries)
{ const OUString& aText = rEntry.GetString();
nMaxTextWidth = std::max<int>(nMaxTextWidth, mpAutoFilterPopup->GetTextWidth(aText) + aText.getLength() * 2);
}
} else
{ // find the longest string, probably it will be the longest rendered text, too // (performance optimization for long lists) auto itMax = aFilterEntries.begin(); for (auto it = itMax; it != aFilterEntries.end(); ++it)
{ int nTextWidth = it->GetString().getLength(); if (nMaxTextWidth < nTextWidth)
{
nMaxTextWidth = nTextWidth;
itMax = it;
}
}
nMaxTextWidth = mpAutoFilterPopup->GetTextWidth(itMax->GetString()) + nMaxTextWidth * 2;
}
// window should be at least as wide as the column, or the longest text + checkbox, scrollbar ... (it is estimated with 70 pixel now) // window should be maximum 1024 pixel wide. int nWindowWidth = std::min<int>(1024, nMaxTextWidth + 70);
nWindowWidth = mpAutoFilterPopup->IncreaseWindowWidthToFitText(nWindowWidth);
nMaxTextWidth = std::max<int>(nMaxTextWidth, nWindowWidth - 70);
if (!bQueryByNonEmpty)
{ for (ScQueryEntry* pEntry : aEntries)
{ if (pEntry && pEntry->eOp == SC_EQUAL)
{
ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
std::for_each(rItems.begin(), rItems.end(), AddSelectedItemString(aSelectedString, aSelectedValue));
}
}
}
// Populate the check box list.
mpAutoFilterPopup->setMemberSize(aFilterEntries.size()); for (auto it = aFilterEntries.begin(); it != aFilterEntries.end(); ++it)
{ // tdf#140745 show (empty) entry on top of the checkbox list if not hidden by filter if (it->GetString().isEmpty() && !it->IsHiddenByFilter())
{ const OUString& aStringVal = it->GetString(); constdouble aDoubleVal = it->GetValue(); bool bSelected = true; if (!aSelectedValue.empty() || !aSelectedString.empty())
bSelected = aSelectedString.count(aStringVal) > 0; elseif (bQueryByNonEmpty)
bSelected = false; // it->IsHiddenByFilter() is always false here so no need to evaluate it
mpAutoFilterPopup->addMember(aStringVal, aDoubleVal, bSelected, false);
aFilterEntries.maStrData.erase(it); break;
}
} for (constauto& rEntry : aFilterEntries)
{ const OUString& aStringVal = rEntry.GetString(); constdouble aDoubleVal = rEntry.GetValue(); constdouble aRDoubleVal = rEntry.GetRoundedValue(); bool bSelected = !rEntry.IsHiddenByFilter();
void ScGridWindow::UpdateAutoFilterFromMenu(AutoFilterMode eMode)
{ // Terminate autofilter popup now when there is no further user input needed bool bColorMode = eMode == AutoFilterMode::TextColor || eMode == AutoFilterMode::BackgroundColor; if (!bColorMode)
mpAutoFilterPopup->terminateAllPopupMenus();
if (eMode == AutoFilterMode::Normal)
{ // Do not recreate autofilter rules if there are no changes from the user
ScCheckListMenuControl::ResultType aResult;
mpAutoFilterPopup->getResult(aResult);
if (aResult == aSaveAutoFilterResult)
{
SAL_INFO("sc.ui", "Apply autofilter to data when entries are the same");
if (!mpAutoFilterPopup->isAllSelected())
{ // Apply autofilter to data
ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
pEntry->bDoQuery = true;
pEntry->nField = rPos.Col();
pEntry->eConnect = SC_AND;
pEntry->eOp = SC_EQUAL;
mrViewData.GetView()->Query(aParam, nullptr, true);
}
return;
}
}
// Remove old entries in auto-filter rules if (!bColorMode)
{
aParam.RemoveAllEntriesByField(rPos.Col());
if (eMode != AutoFilterMode::Clear
&& !(eMode == AutoFilterMode::Normal && mpAutoFilterPopup->isAllSelected()))
{ // Try to use the existing entry for the column (if one exists).
ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
if (!pEntry) // Something went terribly wrong! return;
if (ScTabViewShell::isAnyEditViewInRange(mrViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2)) return;
void getCellGeometry(Point& rScrPos, Size& rScrSize, const ScViewData& rViewData, SCCOL nCol, SCROW nRow, ScSplitPos eWhich)
{ // Get the screen position of the cell.
rScrPos = rViewData.GetScrPos(nCol, nRow, eWhich);
// Get the screen size of the cell.
tools::Long nSizeX, nSizeY;
rViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
rScrSize = Size(nSizeX-1, nSizeY-1);
}
}
void ScGridWindow::LaunchPageFieldMenu( SCCOL nCol, SCROW nRow )
{ if (nCol == 0) // We assume that the page field button is located in cell to the immediate left. return;
// minimum width in pixel if (comphelper::LibreOfficeKit::isActive())
{ const tools::Long nMinLOKWinWidth = o3tl::convert(STD_COL_WIDTH * 13 / 10, o3tl::Length::twip, o3tl::Length::px); if (nSizeX < nMinLOKWinWidth)
nSizeX = nMinLOKWinWidth;
}
weld::TreeView& rFilterBox = mpFilterBox->get_widget(); int nEntryCount = rFilterBox.n_children(); if (nEntryCount > SC_FILTERLISTBOX_LINES)
nEntryCount = SC_FILTERLISTBOX_LINES; auto nHeight = rFilterBox.get_height_rows(nEntryCount);
rFilterBox.set_size_request(-1, nHeight);
Size aSize(rFilterBox.get_preferred_size()); auto nMaxToExpandTo = std::min(nSizeX, static_cast<decltype(nSizeX)>(300)); // do not over do it (Pixel) if (aSize.Width() < nMaxToExpandTo)
aSize.setWidth(nMaxToExpandTo);
aSize.AdjustWidth(4); // add a little margin
nSizeX += 4;
aSize.AdjustHeight(4);
tools::Rectangle aCellRect(rCellRect);
aCellRect.AdjustLeft(-2); // offset the little margin above
SCCOL nCol = rScenRange.aEnd.Col(); // Cell is below the Buttons
SCROW nRow = rScenRange.aStart.Row(); if (nRow == 0)
{
nRow = rScenRange.aEnd.Row() + 1; // Range at very the top -> Button below if (nRow>rDoc.MaxRow()) nRow = rDoc.MaxRow();
bMenuAtTop = false;
}
if (bLOKActive)
{ // aPos is now view-zoom adjusted and in pixels an more importantly this is pixel aligned to the view-zoom, // but once we use this to set the position of the floating window, it has no information of view-zoom level // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, MapMode(MapUnit::MapTwip)) employed in // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position which will again get zoom scaled in the // client (effective double scaling) causing wrong positioning/size. double fZoomX(mrViewData.GetZoomX()); double fZoomY(mrViewData.GetZoomY());
aPos.setX(aPos.getX() / fZoomX);
aPos.setY(aPos.getY() / fZoomY);
nSizeX = nSizeX / fZoomX;
nSizeY = nSizeY / fZoomY;
}
if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
{ auto it = std::lower_bound(aStrings.begin(), aStrings.end(), *pNew, ScTypedStrData::LessCaseSensitive()); if (it != aStrings.end() && ScTypedStrData::EqualCaseSensitive()(*it, *pNew))
nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
} else
{ auto it = std::find_if(aStrings.begin(), aStrings.end(), FindTypedStrData(*pNew, true)); if (it != aStrings.end())
nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
}
}
}
// Do not show an empty selection List:
if ( bEmpty )
{
mpFilterBox.reset();
} else
{
rFilterBox.grab_focus();
if (rFilterBox.n_children())
{ if (nSelPos != -1)
rFilterBox.set_cursor(nSelPos); else
rFilterBox.set_cursor(0);
} // Select only after GrabFocus, so that the focus rectangle gets correct if (nSelPos != -1)
rFilterBox.select(nSelPos); else
rFilterBox.unselect_all();
bool ScGridWindow::TestMouse( const MouseEvent& rMEvt, bool bAction )
{ // MouseEvent buttons must only be checked if bAction==TRUE // to allow changing the mouse pointer in MouseMove, // but not start AutoFill with right button (#74229#). // with bAction==sal_True, SetFillMode / SetDragMode is called
// The simple selection must also be recognized when dragging, // where the Marking flag is set and MarkToSimple won't work anymore.
mrViewData.GetMarkData().MarkToSimple();
}
bNewPointer = true;
}
}
}
if (bRefMode && pView->GetFunctionSet().CheckRefBounds(nPosX, nPosY)) return;
}
nNestedButtonState = ScNestedButtonState::Down;
MouseEventState aState;
HandleMouseButtonDown(rMEvt, aState); if (aState.mbActivatePart)
mrViewData.GetView()->ActivatePart(eWhich);
if ( nNestedButtonState == ScNestedButtonState::Up )
{ // #i41690# If an object is deactivated from MouseButtonDown, it might reschedule, // so MouseButtonUp comes before the MouseButtonDown call is finished. In this case, // simulate another MouseButtonUp call, so the selection state is consistent.
nButtonDown = rMEvt.GetButtons();
FakeButtonUp();
if ( IsTracking() )
EndTracking(); // normally done in VCL as part of MouseButtonUp handling
}
nNestedButtonState = ScNestedButtonState::NONE;
}
void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventState& rState )
{ // We have to check if a context menu is shown and we have an UI // active inplace client. In that case we have to ignore the event. // Otherwise we would crash (context menu has been // opened by inplace client and we would deactivate the inplace client, // the context menu is closed by VCL asynchronously which in the end // would work on deleted objects or the context menu has no parent anymore)
SfxViewShell* pViewSh = mrViewData.GetViewShell();
SfxInPlaceClient* pClient = pViewSh->GetIPClient(); if ( pClient &&
pClient->IsObjectInPlaceActive() &&
vcl::IsInPopupMenuExecute() ) return;
aCurMousePos = rMEvt.GetPosPixel();
// Filter popup is ended with its own mouse click, not when clicking into the Grid Window, // so the following query is no longer necessary:
ClickExtern(); // deletes FilterBox when available
HideNoteOverlay();
bEEMouse = false;
ScModule* pScMod = ScModule::get(); if (pScMod->IsModalMode(&mrViewData.GetSfxDocShell())) return;
constbool bWasMouseCaptured = IsMouseCaptured();
SAL_WARN_IF(bWasMouseCaptured, "sc.ui", "Is there a scenario where the mouse is captured before mouse down?");
pScActiveViewShell = mrViewData.GetViewShell(); // if left is clicked
nScClickMouseModifier = rMEvt.GetModifier(); // to always catch a control click
// DeactivateIP does only happen when MarkListHasChanged
// An error message can show up during GrabFocus call // (for instance when renaming tables per sheet title)
if ( !nButtonDown || !bDouble ) // single (first) click is always valid
nButtonDown = rMEvt.GetButtons(); // set nButtonDown first, so StopMarking works
// #i31846# need to cancel a double click if the first click has set the "ignore" state, // but a single (first) click is always valid if ( nMouseStatus == SC_GM_IGNORE && bDouble )
{
nButtonDown = 0;
nMouseStatus = SC_GM_NONE; return;
}
if ( bDetective ) // Detectiv fill mode
{ if ( rMEvt.IsLeft() && !rMEvt.GetModifier() )
{
Point aPos = rMEvt.GetPosPixel();
SCCOL nPosX;
SCROW nPosY;
mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
if ( nPosX >= nStartCol && nPosX <= nEndCol &&
nPosY >= nEditRow && nPosY <= nEndRow )
{ // when clicking in the table EditView, always reset the focus if (bFormulaMode) // otherwise this has already happen above
GrabFocus();
if (pScMod->GetIsWaterCan())
{ //! what's up with the Mac ??? if ( rMEvt.GetModifier() + rMEvt.GetButtons() == MOUSE_RIGHT )
{
nMouseStatus = SC_GM_WATERUNDO; return;
}
}
// Order that matches the displayed Cursor: // RangeFinder, AutoFill, PageBreak, Drawing
// in the tiled rendering case, single clicks into drawing objects take // precedence over bEditMode if (((!bFormulaMode && !bEditMode) || bIsTiledRendering) && rMEvt.IsLeft())
{ if ( !bCrossPointer && DrawMouseButtonDown(rMEvt) )
{ return;
}
mrViewData.GetViewShell()->SetDrawShell( false ); // no Draw-object selected
// FIXME: this is to limit the number of rows handled in the Online // to 1000; this will be removed again when the performance // bottlenecks are sorted out if ( comphelper::LibreOfficeKit::isActive() && nPosY > MAXTILEDROW - 1 )
{
nButtonDown = 0;
nMouseStatus = SC_GM_NONE; return;
}
// Auto filter / pivot table / data select popup. This shouldn't activate the part.
// show in the merged cells the filter of the first cell (nPosX instead of nRealPosX) const ScMergeFlagAttr* pRealPosAttr = rDoc.GetAttr(nPosX, nRealPosY, nTab, ATTR_MERGE_FLAG);
if (!bAutoFilterDisable && pRealPosAttr->HasAutoFilter())
{
pScMod->InputEnterHandler(); if (DoAutoFilterButton(nPosX, nRealPosY, rMEvt)) return;
}
if ( bListValButton )
{
tools::Rectangle aButtonRect = GetListValButtonRect( aListValPos ); if ( aButtonRect.Contains( aPos ) )
{ // tdf#149609 if we captured the mouse in the course of this function // release it before showing the data select menu to undo any unhelpful // seleng capture if (!bWasMouseCaptured && IsMouseCaptured())
ReleaseMouse();
constbool lokReadOnly = comphelper::LibreOfficeKit::isActive() && pViewSh->IsLokReadOnlyView(); if (lokReadOnly) return; // Return as if the action was performed, so the flow is not affected.
nMouseStatus = SC_GM_FILTER; // not set in DoAutoFilterMenue for bDataSelect
rState.mbActivatePart = false; return;
}
}
}
// scenario selection
ScRange aScenRange; if ( rMEvt.IsLeft() && HasScenarioButton( aPos, aScenRange ) )
{ // tdf#149609 if we captured the mouse in the course of this function // release it before showing the data scenario menu to undo any unhelpful // seleng capture if (!bWasMouseCaptured && IsMouseCaptured())
ReleaseMouse();
DoScenarioMenu( aScenRange );
// Scenario selection comes from MouseButtonDown: // The next MouseMove on the FilterBox is like a ButtonDown
nMouseStatus = SC_GM_FILTER; return;
}
// double click started ?
// StopMarking can be called from DrawMouseButtonDown
if ( nMouseStatus != SC_GM_IGNORE && !bRefMode )
{ if ( bDouble && !bCrossPointer )
{ if (nMouseStatus == SC_GM_TABDOWN)
nMouseStatus = SC_GM_DBLDOWN;
} else
nMouseStatus = SC_GM_TABDOWN;
}
// links in the edit cell
bool bAlt = rMEvt.IsMod2(); if ( !bAlt && rMEvt.IsLeft() && ScGlobal::ShouldOpenURL() &&
GetEditUrl(rMEvt.GetPosPixel()) ) // click on link: do not move cursor
{
SetPointer( PointerStyle::RefHand );
nMouseStatus = SC_GM_URLDOWN; // also only execute when ButtonUp return;
}
// SelMouseButtonDown on the View is still setting the bMoveIsShift flag if ( mrViewData.GetView()->SelMouseButtonDown( rMEvt ) )
{ if (IsMouseCaptured())
{ // Tracking instead of CaptureMouse, so it can be canceled cleanly //! Someday SelectionEngine should call StartTracking on its own!?!
ReleaseMouse();
StartTracking();
}
mrViewData.GetMarkData().SetMarking(true); return;
}
}
void ScGridWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
aCurMousePos = rMEvt.GetPosPixel();
ScDocument& rDoc = mrViewData.GetDocument();
ScMarkData& rMark = mrViewData.GetMarkData(); // #i41690# detect a MouseButtonUp call from within MouseButtonDown // (possible through Reschedule from storing an OLE object that is deselected)
if ( nNestedButtonState == ScNestedButtonState::Down )
nNestedButtonState = ScNestedButtonState::Up;
if (nButtonDown != rMEvt.GetButtons())
nMouseStatus = SC_GM_IGNORE; // reset and return
bool bAlt = rMEvt.IsMod2(); if ( !bAlt && !bRefMode && nMouseStatus == SC_GM_URLDOWN )
{ // Only execute on ButtonUp, if ButtonDown also was done on a URL
OUString aName, aUrl, aTarget;
SCCOL nUrlCellX; if (GetEditUrl(rMEvt.GetPosPixel(), &aName, &aUrl, &aTarget, &nUrlCellX))
{
nMouseStatus = SC_GM_NONE; // Ignore double-click bool isTiledRendering = comphelper::LibreOfficeKit::isActive(); // ScGlobal::OpenURL() only understands Calc A1 style syntax. // Convert it to Calc A1 before calling OpenURL(). if (rDoc.GetAddressConvention() == formula::FormulaGrammar::CONV_OOO)
{ if (aUrl.startsWith("#")) {
ScGlobal::OpenURL(aUrl, aTarget, isTiledRendering); return;
} // On a mobile device view there is no ctrl+click and for hyperlink popup // the cell coordinates must be sent along with click position for elegance
ScTabViewShell* pViewShell = mrViewData.GetViewShell(); if (isTiledRendering && pViewShell &&
(pViewShell->isLOKMobilePhone() || pViewShell->isLOKTablet()))
{
aPos = rMEvt.GetPosPixel();
mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
OString aCursor
= pViewShell->GetViewData().describeCellCursorAt(nUrlCellX, nPosY); double fPPTX = pViewShell->GetViewData().GetPPTX(); int mouseX = aPos.X() / fPPTX; int mouseY = aPos.Y() / fPPTX;
OString aMsg(aUrl.toUtf8() + " coordinates: " + aCursor + ", "
+ OString::number(mouseX) + ", " + OString::number(mouseY));
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
} else
ScGlobal::OpenURL(aUrl, aTarget);
} else
{
ScAddress aTempAddr;
ScAddress::ExternalInfo aExtInfo;
ScRefFlags nRes = aTempAddr.Parse(aUrl, rDoc, rDoc.GetAddressConvention(), &aExtInfo); if (!(nRes & ScRefFlags::VALID))
{ // Not a reference string. Pass it through unmodified.
ScGlobal::OpenURL(aUrl, aTarget); return;
}
OUStringBuffer aBuf; if (aExtInfo.mbExternal)
{ // External reference.
ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); const OUString* pStr = pRefMgr->getExternalFileName(aExtInfo.mnFileId); if (pStr)
aBuf.append(*pStr);
// SelMouseButtonDown is called only for left button, but SelMouseButtonUp would return // sal_True for any call, so IsLeft must be checked here, too.
if ( !(rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt )) ) return;
mrViewData.GetView()->SelectionChanged();
SfxDispatcher* pDisp = mrViewData.GetViewShell()->GetDispatcher(); bool bFormulaMode = pScMod->IsFormulaMode();
OSL_ENSURE( pDisp || bFormulaMode, "Cursor moved on inactive View ?" );
// #i14927# execute SID_CURRENTCELL (for macro recording) only if there is no // multiple selection, so the argument string completely describes the selection, // and executing the slot won't change the existing selection (executing the slot // here and from a recorded macro is treated equally) if ( pDisp && !bFormulaMode && !rMark.IsMultiMarked() )
{
OUString aAddr; // CurrentCell if( rMark.IsMarked() )
{ const ScRange& aScRange = rMark.GetMarkArea();
aAddr = aScRange.Format(rDoc, ScRefFlags::RANGE_ABS); if ( aScRange.aStart == aScRange.aEnd )
{ // make sure there is a range selection string even for a single cell
aAddr += ":" + aAddr;
}
//! SID_MARKAREA does not exist anymore ??? //! What happens when selecting with the cursor ???
} else// only move cursor
{
ScAddress aScAddress( mrViewData.GetCurX(), mrViewData.GetCurY(), 0 );
aAddr = aScAddress.Format(ScRefFlags::ADDR_ABS);
}
SfxStringItem aPosItem( SID_CURRENTCELL, aAddr ); // We don't want to align to the cursor position because if the // cell cursor isn't visible after making selection, it would jump // back to the origin of the selection where the cell cursor is.
SfxBoolItem aAlignCursorItem( FN_PARAM_2, false );
pDisp->ExecuteList(SID_CURRENTCELL,
SfxCallMode::SLOT | SfxCallMode::RECORD,
{ &aPosItem, &aAlignCursorItem });
if (rMEvt.IsLeaveWindow() && mpNoteOverlay && !mpNoteOverlay->IsByKeyboard())
HideNoteOverlay();
ScModule* pScMod = ScModule::get(); if (pScMod->IsModalMode(&mrViewData.GetSfxDocShell())) return;
// If the Drag&Drop is started in the edit mode then sadly nothing else is kept if (bEEMouse && nButtonDown && !rMEvt.GetButtons())
{
bEEMouse = false;
nButtonDown = 0;
nMouseStatus = SC_GM_NONE; return;
}
if (nMouseStatus == SC_GM_IGNORE) return;
if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode -> only what for Up return;
if ( mrViewData.GetViewShell()->IsAuditShell() ) // Detective Fill Mode
{
SetPointer( PointerStyle::Fill ); return;
}
bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
if ( !nButtonDown && mrViewData.IsPagebreakMode() )
{
sal_uInt16 nBreakType = HitPageBreak( rMEvt.GetPosPixel(), nullptr, nullptr, nullptr ); if (nBreakType != 0 )
{
PointerStyle eNew = PointerStyle::Arrow; switch ( nBreakType )
{ case SC_PD_RANGE_L: case SC_PD_RANGE_R: case SC_PD_BREAK_H:
eNew = PointerStyle::ESize; break; case SC_PD_RANGE_T: case SC_PD_RANGE_B: case SC_PD_BREAK_V:
eNew = PointerStyle::SSize; break; case SC_PD_RANGE_TL: case SC_PD_RANGE_BR:
eNew = PointerStyle::SESize; break; case SC_PD_RANGE_TR: case SC_PD_RANGE_BL:
eNew = PointerStyle::NESize; break;
}
SetPointer( eNew );
bCross = true;
}
}
// Show fill cursor?
if ( !bFormulaMode && !nButtonDown ) if (TestMouse( rMEvt, false ))
bCross = true;
if ( nButtonDown && mrViewData.IsAnyFillMode() )
{
SetPointer( PointerStyle::Cross );
bCross = true;
nScFillModeMouseModifier = rMEvt.GetModifier(); // evaluated for AutoFill and Matrix
}
if (!bCross)
{ bool bAlt = rMEvt.IsMod2();
if (bEditMode) // First has to be in edit mode!
SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow ); elseif ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() &&
GetEditUrl(rMEvt.GetPosPixel()) )
SetPointer( PointerStyle::RefHand ); elseif ( DrawMouseMove(rMEvt) ) // Reset pointer return;
}
}
// In LOK case, avoid spurious "leavingwindow" mouse move events which has negative coordinates. // Such events occur for some reason when a user is selecting a range, (even when not leaving the view area) // with one or more other viewers in that sheet. bool bSkipSelectionUpdate = comphelper::LibreOfficeKit::isActive() &&
rMEvt.IsLeaveWindow() && (aCurMousePos.X() < 0 || aCurMousePos.Y() < 0);
if (!bSkipSelectionUpdate)
mrViewData.GetView()->GetSelEngine()->SelMouseMove( rMEvt );
}
staticvoid lcl_InitMouseEvent(css::awt::MouseEvent& rEvent, const MouseEvent& rEvt)
{
rEvent.Modifiers = 0; if ( rEvt.IsShift() )
rEvent.Modifiers |= css::awt::KeyModifier::SHIFT; if ( rEvt.IsMod1() )
rEvent.Modifiers |= css::awt::KeyModifier::MOD1; if ( rEvt.IsMod2() )
rEvent.Modifiers |= css::awt::KeyModifier::MOD2; if ( rEvt.IsMod3() )
rEvent.Modifiers |= css::awt::KeyModifier::MOD3;
rEvent.Buttons = 0; if ( rEvt.IsLeft() )
rEvent.Buttons |= css::awt::MouseButton::LEFT; if ( rEvt.IsRight() )
rEvent.Buttons |= css::awt::MouseButton::RIGHT; if ( rEvt.IsMiddle() )
rEvent.Buttons |= css::awt::MouseButton::MIDDLE;
void ScGridWindow::Tracking( const TrackingEvent& rTEvt )
{ // Since the SelectionEngine does not track, the events have to be // handed to the different MouseHandler...
const MouseEvent& rMEvt = rTEvt.GetMouseEvent();
if ( rTEvt.IsTrackingCanceled() ) // Cancel everything...
{ if (!mrViewData.GetView()->IsInActivatePart() && !ScModule::get()->IsRefDialogOpen())
{ if (bDPMouse)
bDPMouse = false; // Paint for each bDragRect if (bDragRect)
{
bDragRect = false;
UpdateDragRectOverlay();
} if (bRFMouse)
{
RFMouseMove( rMEvt, true ); // Not possible to cancel properly...
bRFMouse = false;
} if (nPagebreakMouse)
{
bPagebreakDrawn = false;
UpdateDragRectOverlay();
nPagebreakMouse = SC_PD_NONE;
}
SetPointer( PointerStyle::Arrow );
StopMarking();
MouseButtonUp( rMEvt ); // With status SC_GM_IGNORE from StopMarking
bool bRefMode = mrViewData.IsRefMode(); if (bRefMode)
ScModule::get()->EndReference(); // Do not let the Dialog remain minimized
}
} elseif ( rTEvt.IsTrackingEnded() )
{ if (!comphelper::LibreOfficeKit::isActive())
{ // MouseButtonUp always with matching buttons (eg for test tool, # 63148 #) // The tracking event will indicate if it was completed and not canceled.
MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
rMEvt.GetMode(), nButtonDown, rMEvt.GetModifier() );
MouseButtonUp( aUpEvt );
}
} else
MouseMove( rMEvt );
}
void ScGridWindow::Command( const CommandEvent& rCEvt )
{ // The command event is send to the window after a possible context // menu from an inplace client is closed. Now we have the chance to // deactivate the inplace client without any problem regarding parent // windows and code on the stack.
CommandEventId nCmd = rCEvt.GetCommand();
ScTabViewShell* pTabViewSh = mrViewData.GetViewShell();
SfxInPlaceClient* pClient = pTabViewSh->GetIPClient(); if ( pClient &&
pClient->IsObjectInPlaceActive() &&
nCmd == CommandEventId::ContextMenu )
{
pTabViewSh->DeactivateOle(); return;
}
ScModule* pScMod = ScModule::get();
OSL_ENSURE( nCmd != CommandEventId::StartDrag, "ScGridWindow::Command called with CommandEventId::StartDrag" );
if (nCmd == CommandEventId::ModKeyChange)
{
Window::Command(rCEvt); return;
}
if ( nCmd == CommandEventId::StartExtTextInput ||
nCmd == CommandEventId::EndExtTextInput ||
nCmd == CommandEventId::ExtTextInput ||
nCmd == CommandEventId::CursorPos ||
nCmd == CommandEventId::QueryCharPosition )
{ bool bEditView = mrViewData.HasEditView( eWhich ); if (!bEditView)
{ // only if no cell editview is active, look at drawview
SdrView* pSdrView = mrViewData.GetView()->GetScDrawView(); if ( pSdrView )
{
OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView(); if ( pOlView && pOlView->GetWindow() == this )
{
pOlView->Command( rCEvt ); return; // done
}
}
}
if ( nCmd == CommandEventId::CursorPos && !bEditView )
{ // CURSORPOS may be called without following text input, // to set the input method window position // -> input mode must not be started, // manually calculate text insert position if not in input mode
lcl_SetTextCursorPos( mrViewData, eWhich, this ); return;
}
if ( nCmd == CommandEventId::PasteSelection )
{ if ( bEEMouse )
{ // EditEngine handles selection in MouseButtonUp - no action // needed in command handler
} else
{
PasteSelection( rCEvt.GetMousePosPixel() );
} return;
}
if ( nCmd == CommandEventId::InputLanguageChange )
{ // #i55929# Font and font size state depends on input language if nothing is selected, // so the slots have to be invalidated when the input language is changed.
if (nCmd == CommandEventId::GesturePan)
{ bool bDone = mrViewData.GetView()->GesturePanCommand(rCEvt); if (!bDone)
Window::Command(rCEvt); return;
}
if (nCmd == CommandEventId::GestureZoom)
{ bool bDone = mrViewData.GetView()->GestureZoomCommand(rCEvt); if (!bDone)
Window::Command(rCEvt); return;
}
// #i7560# FormulaMode check is below scrolling - scrolling is allowed during formula input bool bDisable = pScMod->IsFormulaMode() ||
pScMod->IsModalMode(&mrViewData.GetSfxDocShell()); if (bDisable) return;
if (nCmd != CommandEventId::ContextMenu || pScMod->GetIsWaterCan()) return;
if (mrViewData.IsAnyFillMode())
{
mrViewData.GetView()->StopRefMode();
mrViewData.ResetFillMode();
}
ReleaseMouse();
StopMarking();
Point aPosPixel = rCEvt.GetMousePosPixel();
Point aMenuPos = aPosPixel;
bool bPosIsInEditView = mrViewData.HasEditView(eWhich);
SCCOL nCellX = -1;
SCROW nCellY = -1;
mrViewData.GetPosFromPixel(aPosPixel.X(), aPosPixel.Y(), eWhich, nCellX, nCellY); // GetPosFromPixel ignores the fact that when editing a cell, the cell might grow to cover // other rows/columns. In addition, the mouse might now be outside the edited cell. if (bPosIsInEditView)
{ if (nCellX >= mrViewData.GetEditViewCol() && nCellX <= mrViewData.GetEditEndCol())
nCellX = mrViewData.GetEditViewCol(); else
bPosIsInEditView = false;
if (!bPosIsInEditView)
{ // Close the edit view when moving outside of the edited cell // to avoid showing the edit popup, or providing the wrong EditView to spellcheck.
ScInputHandler* pHdl = pScMod->GetInputHdl(); if (pHdl)
pHdl->EnterHandler();
}
}
if ( bMouse )
{
ScDocument& rDoc = mrViewData.GetDocument();
SCTAB nTab = mrViewData.GetTabNo(); const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab); bool bSelectAllowed = true; if ( pProtect && pProtect->isProtected() )
{ // This sheet is protected. Check if a context menu is allowed on this cell. bool bCellProtected = rDoc.HasAttrib(nCellX, nCellY, nTab, nCellX, nCellY, nTab, HasAttrFlags::Protected); bool bSelProtected = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS); bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
if (bCellProtected)
bSelectAllowed = bSelProtected; else
bSelectAllowed = bSelUnprotected;
} if (!bSelectAllowed) // Selecting this cell is not allowed, neither is context menu. return;
if (mpSpellCheckCxt)
{ // Find the first string to the left for spell checking in case the current cell is empty.
ScAddress aPos(nCellX, nCellY, nTab);
ScRefCellValue aSpellCheckCell(rDoc, aPos); while (!bPosIsInEditView && aSpellCheckCell.getType() == CELLTYPE_NONE)
{ // Loop until we get the first non-empty cell in the row.
aPos.IncCol(-1); if (aPos.Col() < 0) break;
// Is there possibly a misspelled word somewhere in the cell? // A "yes" does not mean that the word under the mouse pointer is wrong though.
bSpellError = (mpSpellCheckCxt->isMisspelled(nColSpellError, nCellY)); // Avoid situations where selecting the cell-with-wrong-spelling would be bad if (bSpellError)
{ // When the mouse is over an empty cell, text with spelling errors // potentially could have overflowed underneath the mouse pointer if (nColSpellError != nCellX)
{ // If the mouse is over a selected cell, only consider spell-checking // if the cell with the misspelling is also selected. tdf#157038 if (mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY))
bSpellError = mrViewData.GetMarkData().IsCellMarked(nColSpellError, nCellY);
}
}
}
// #i18735# First select the item under the mouse pointer. // This can change the selection, and the view state (edit mode, etc).
SelectForContextMenu(aPosPixel, bSpellError ? nColSpellError : nCellX, nCellY);
}
if ( !bEdit )
{ // Edit cell with spelling errors? // tdf#127341 the formerly used GetEditUrl(aPosPixel) additionally // to bSpellError activated EditMode here for right-click on URL // which prevents the regular context-menu from appearing. Since this // is more expected than the context-menu for editing an URL, I removed // this. If this was wanted and can be argued it might be re-activated. // For now, reduce to spelling errors - as the original comment above // suggests. if (bMouse && bSpellError)
{ // GetEditUrlOrError has already moved the Cursor
pScMod->SetInputMode( SC_INPUT_TABLE );
bEdit = mrViewData.HasEditView(eWhich); // Did it work?
OSL_ENSURE( bEdit, "Can not be switched in edit mode" );
}
} if ( bEdit )
{
EditView* pEditView = mrViewData.GetEditView( eWhich ); // is then not 0
if ( !bMouse )
{
vcl::Cursor* pCur = pEditView->GetCursor(); if ( pCur )
{
Point aLogicPos = pCur->GetPos(); // use the position right of the cursor (spell popup is opened if // the cursor is before the word, but not if behind it)
aLogicPos.AdjustX(pCur->GetWidth() );
aLogicPos.AdjustY(pCur->GetHeight() / 2 ); // center vertically
aMenuPos = LogicToPixel( aLogicPos );
}
}
// if edit mode was just started above, online spelling may be incomplete
pEditView->getEditEngine().CompleteOnlineSpelling();
// IsCursorAtWrongSpelledWord could be used for !bMouse // if there was a corresponding ExecuteSpellPopup call
if (bSpellError)
{ // On OS/2 when clicking next to the Popup menu, the MouseButtonDown // comes before the end of menu execute, thus the SetModified has to // be done prior to this (Bug #40968#)
ScInputHandler* pHdl = pScMod->GetInputHdl(); if (pHdl)
pHdl->SetModified();
// Only done/shown if a misspelled word is actually under the mouse pointer.
Link<SpellCallbackInfo&,void> aLink = LINK( this, ScGridWindow, PopupSpellingHdl );
bDone = pEditView->ExecuteSpellPopup(aMenuPos, aLink);
// If the spelling is corrected, stop editing to flush any cached spelling info. // Or, if no misspelled word at this position, and it wasn't initially in edit mode, // then exit the edit mode in order to get the full context popup (not edit popup). if (pHdl && (pHdl->GetEditString() != sOldText
|| (!bDone && !bPosIsInEditView)))
{
pHdl->EnterHandler();
}
if (!bDone && nColSpellError != nCellX)
{ // NOTE: This call can change the selection, and the view state (edit mode, etc).
SelectForContextMenu(aPosPixel, nCellX, nCellY);
}
}
} elseif ( !bMouse )
{ // non-edit menu by keyboard -> use lower right of cell cursor position
ScDocument& rDoc = mrViewData.GetDocument();
SCTAB nTabNo = mrViewData.GetTabNo(); bool bLayoutIsRTL = rDoc.IsLayoutRTL(nTabNo);
ScTabViewShell* pViewSh = mrViewData.GetViewShell(); if (pViewSh)
{ // Is a draw object selected?
SdrView* pDrawView = pViewSh->GetScDrawView(); if (pDrawView && pDrawView->GetMarkedObjectList().GetMarkCount() != 0)
{ // #100442#; the context menu should open in the middle of the selected objects
tools::Rectangle aSelectRect(LogicToPixel(pDrawView->GetAllMarkedBoundRect()));
aMenuPos = aSelectRect.Center();
}
}
}
if (bDone) return;
SfxDispatcher::ExecutePopup( this, &aMenuPos );
}
void ScGridWindow::SelectForContextMenu( const Point& rPosPixel, SCCOL nCellX, SCROW nCellY )
{ // #i18735# if the click was outside of the current selection, // the cursor is moved or an object at the click position selected. // (see SwEditWin::SelectMenuPosition in Writer)
Point aTextPos = PixelToLogic( rPosPixel ); if (rEditEngine.IsEffectivelyVertical()) // have to manually transform position
{
aTextPos -= aOutputArea.TopRight();
tools::Long nTemp = -aTextPos.X();
aTextPos.setX( aTextPos.Y() );
aTextPos.setY( nTemp );
} else
aTextPos -= aOutputArea.TopLeft();
aTextPos += aVisArea.TopLeft(); // position in the edit document
ESelection aCompare(rEditEngine.FindDocPosition(aTextPos));
ESelection aSelection = pEditView->GetSelection();
aSelection.Adjust(); // needed for IsLess/IsGreater if ( aCompare < aSelection || aCompare > aSelection )
{ // clicked outside the selected text - deselect and move text cursor
MouseEvent aEvent( rPosPixel );
pEditView->MouseButtonDown( aEvent );
pEditView->MouseButtonUp( aEvent );
pScMod->InputSelection( pEditView );
}
return; // clicked within the edit view - keep edit mode
} else
{ // outside of the edit view - end edit mode, regardless of cell selection, then continue
pScMod->InputEnterHandler();
}
}
// check draw text edit mode
Point aLogicPos = PixelToLogic( rPosPixel ); // after cell edit mode is ended if ( pDrawView && pDrawView->GetTextEditObject() && pDrawView->GetTextEditOutlinerView() )
{
OutlinerView* pOlView = pDrawView->GetTextEditOutlinerView();
tools::Rectangle aOutputArea = pOlView->GetOutputArea(); if ( aOutputArea.Contains( aLogicPos ) )
{ // handle selection within the OutlinerView
Point aTextPos = aLogicPos; if ( rOutliner.IsVertical() ) // have to manually transform position
{
aTextPos -= aOutputArea.TopRight();
tools::Long nTemp = -aTextPos.X();
aTextPos.setX( aTextPos.Y() );
aTextPos.setY( nTemp );
} else
aTextPos -= aOutputArea.TopLeft();
aTextPos += aVisArea.TopLeft(); // position in the edit document
ESelection aCompare(rEditEngine.FindDocPosition(aTextPos));
ESelection aSelection = pOlView->GetSelection();
aSelection.Adjust(); // needed for IsLess/IsGreater if ( aCompare < aSelection || aCompare > aSelection )
{ // clicked outside the selected text - deselect and move text cursor // use DrawView to allow extra handling there (none currently)
MouseEvent aEvent( rPosPixel );
pDrawView->MouseButtonDown( aEvent, GetOutDev() );
pDrawView->MouseButtonUp( aEvent, GetOutDev() );
}
return; // clicked within the edit area - keep edit mode
} else
{ // Outside of the edit area - end text edit mode, then continue. // DrawDeselectAll also ends text edit mode and updates the shells. // If the click was on the edited object, it will be selected again below.
pView->DrawDeselectAll();
}
}
// hide the border around the copy source
mrViewData.SetPasteMode( ScPasteFlags::NONE ); // Clear CopySourceOverlay in each window of a split/frozen tabview
mrViewData.GetView()->UpdateCopySourceOverlay(); return;
} // if semi-modeless SfxChildWindow dialog above, then no KeyInputs: elseif( !mrViewData.IsAnyFillMode() )
{ if (rKeyCode.GetCode() == KEY_ESCAPE)
{
mrViewData.SetPasteMode( ScPasteFlags::NONE ); // Clear CopySourceOverlay in each window of a split/frozen tabview
mrViewData.GetView()->UpdateCopySourceOverlay();
} // query for existing note marker before calling ViewShell's keyboard handling // which may remove the marker bool bHadKeyMarker(mpNoteOverlay && mpNoteOverlay->IsByKeyboard());
ScTabViewShell* pViewSh = mrViewData.GetViewShell();
if (mrViewData.GetDocShell().GetProgress()) return;
if (!mrViewData.GetView()->IsDrawSelMode() && !DrawHasMarkedObj()) // No entries in draw mode
{ //! check DrawShell !!! if (pViewSh->TabKeyInput(rKEvt)) return;
} else if (pViewSh->SfxViewShell::KeyInput(rKEvt)) // from SfxViewShell return;
vcl::KeyCode aCode = rKEvt.GetKeyCode(); if ( aCode.GetCode() == KEY_ESCAPE && aCode.GetModifier() == 0 )
{ if ( bHadKeyMarker )
HideNoteOverlay(); else
pViewSh->Escape(); return;
} if ( aCode.GetCode() == KEY_F1 && aCode.GetModifier() == KEY_MOD1 )
{ // ctrl-F1 shows or hides the note or redlining info for the cursor position // (hard-coded because F1 can't be configured)
staticbool lcl_TestScenarioRedliningDrop( const ScDocument* pDoc, const ScRange& aDragRange)
{ // Test, if a scenario is affected by a drop when turing on RedLining, bool bReturn = false;
SCTAB nTab = aDragRange.aStart.Tab();
SCTAB nTabCount = pDoc->GetTableCount();
if ( rData.aLinkDoc != aThisName )
nRet = rEvt.mnAction;
} elseif (!rData.aJumpTarget.isEmpty())
{ // internal bookmarks (from Navigator) // local jumps from an unnamed document are possible only within a document
if ( nRet )
{ // Simple check for protection: It's not known here if the drop will result // in cells or drawing objects (some formats can be both) and how many cells // the result will be. But if IsFormatEditable for the drop cell position // is sal_False (ignores matrix formulas), nothing can be pasted, so the drop // can already be rejected here.
// workaround for wrong nDndAction on Windows when pressing solely // the Alt key during drag and drop; // can be removed after #i79215# has been fixed if ( meDragInsertMode != INS_NONE )
{
bIsMove = ( nDndAction & DND_ACTION_MOVE && !bIsNavi );
}
bool bIsLink = ( nDndAction == DND_ACTION_LINK );
ScRange aSource = pTransObj->GetRange();
// only use visible tab from source range - when dragging within one table, // all selected tables at the time of dropping are used (handled in MoveBlockTo)
SCTAB nSourceTab = pTransObj->GetVisibleTab();
aSource.aStart.SetTab( nSourceTab );
aSource.aEnd.SetTab( nSourceTab );
/* NOTE: AcceptPrivateDrop() already checked for filtered conditions during * dragging and adapted drawing of the selection frame. We check here * (again) because this may actually also be called from PasteSelection(), * we would have to duplicate determination of flags and destination range * and would lose the context of the "filtered destination is OK" cases
* below, which is already awkward enough as is. */
// Don't move filtered source. bool bFiltered = (bIsMove && pTransObj->HasFilteredRows()); if (!bFiltered)
{ if (pSourceDoc != &rThisDoc && ((nFlags & ScDragSrc::Table) ||
(!bIsLink && meDragInsertMode == INS_NONE)))
{ // Nothing. Either entire sheet to be dropped, or the one case // where PasteFromClip() is to be called that handles a filtered // destination itself. Drag-copy from another document without // inserting cells.
} else // Don't copy or move to filtered destination.
bFiltered = ScViewUtil::HasFiltered(aDest, rThisDoc);
}
bool bDone = false;
if (!bFiltered && pSourceDoc == &rThisDoc)
{ if (nFlags & ScDragSrc::Table) // whole sheet?
{ if ( rThisDoc.IsDocEditable() )
{
SCTAB nSrcTab = aSource.aStart.Tab();
mrViewData.GetDocShell().MoveTable( nSrcTab, nThisTab, !bIsMove, true ); // with Undo
pView->SetTabNo( nThisTab, true );
bDone = true;
}
} else// move/copy block
{
OUString aChartName; if (rThisDoc.HasChartAtPoint( nThisTab, rLogicPos, aChartName ))
{
OUString aRangeName(aSource.Format(rThisDoc, ScRefFlags::RANGE_ABS_3D,
rThisDoc.GetAddressConvention()));
SfxStringItem aNameItem( SID_CHART_NAME, aChartName );
SfxStringItem aRangeItem( SID_CHART_SOURCE, aRangeName );
sal_uInt16 nId = bIsMove ? SID_CHART_SOURCE : SID_CHART_ADDSOURCE;
mrViewData.GetDispatcher().ExecuteList(nId,
SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
{ &aRangeItem, &aNameItem });
bDone = true;
} elseif ( rThisDoc.GetDPAtCursor( nDestPosX, nDestPosY, nThisTab ) )
{ // drop on DataPilot table: try to sort, fail if that isn't possible
rDocSh.GetUndoManager()->LeaveListAction();
}
} else
{ //! HasSelectedBlockMatrixFragment without selected sheet? //! or don't start dragging on a part of a matrix
// If the mouse down was inside a visible note window, ignore it and // leave it up to the ScPostIt to handle it
SdrView* pDrawView = mrViewData.GetViewShell()->GetScDrawView(); if (pDrawView)
{ const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList(); const size_t nCount = rMarkList.GetMarkCount(); for (size_t i = 0; i < nCount; ++i)
{
SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); if (pObj && pObj->GetLogicRect().Contains(aLogicPos))
{ // Inside an active drawing object. Bail out. return;
}
}
}
ScSelectionTransferObj* pOwnSelection = ScModule::get()->GetSelectionTransfer(); if ( pOwnSelection )
{ // within Calc
// keep a reference to the data in case the selection is changed during paste
rtl::Reference<ScTransferObj> pCellTransfer = pOwnSelection->GetCellData(); if ( pCellTransfer )
{
DropTransferObj( pCellTransfer.get(), nPosX, nPosY, aLogicPos, DND_ACTION_COPY );
} else
{ // keep a reference to the data in case the selection is changed during paste
rtl::Reference<ScDrawTransferObj> pDrawTransfer = pOwnSelection->GetDrawData(); if ( pDrawTransfer )
{ // bSameDocClipboard argument for PasteDraw is needed // because only DragData is checked directly inside PasteDraw
mrViewData.GetView()->PasteDraw(
aLogicPos, pDrawTransfer->GetModel(), false,
pDrawTransfer->GetShellID(), SfxObjectShell::CreateShellID(&mrViewData.GetDocShell()));
}
}
} else
{ // get selection from system
TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromPrimarySelection()); const uno::Reference<datatransfer::XTransferable>& xTransferable = aDataHelper.GetTransferable(); if ( xTransferable.is() )
{
SotClipboardFormatId nFormatId = lcl_GetDropFormatId( xTransferable, true ); if ( nFormatId != SotClipboardFormatId::NONE )
mrViewData.GetView()->PasteDataFormat( nFormatId, xTransferable, nPosX, nPosY, &aLogicPos );
}
}
}
void ScGridWindow::UpdateEditViewPos()
{ if (!mrViewData.HasEditView(eWhich)) return;
// #i122149# do not use old GetChangedArea() which used polygon-based Regions, but use // the region-band based new version; anyways, only rectangles are added
vcl::Region aChangedRegion( aOutputData.GetChangedAreaRegion() ); // logic (PixelToLogic) if(!aChangedRegion.IsEmpty())
{
Invalidate(aChangedRegion);
}
CheckNeedsRepaint(); // #i90362# used to be called via Draw() - still needed here
}
void ScGridWindow::GetFocus()
{
ScTabViewShell* pViewShell = mrViewData.GetViewShell();
pViewShell->SetFormShellAtTop( false ); // focus in GridWindow -> FormShell no longer on top
if (pViewShell->HasAccessibilityObjects())
pViewShell->BroadcastAccessibility(ScAccGridWinFocusGotHint(eWhich));
if (!ScModule::get()->IsFormulaMode())
{
pViewShell->UpdateInputHandler(); // StopMarking(); // If Dialog (error), because then no ButtonUp // MO: only when not in RefInput mode // -> GetFocus/MouseButtonDown order on Mac
}
// corner is hit only if the mouse is within the cell
sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count()); for (sal_uInt16 i=nCount; i;)
{ // search backwards so that the last repainted frame is found
--i;
ScRangeFindData& rData = pRangeFinder->GetObject(i); if ( rData.aRef.Contains(aAddr) )
{ if (pIndex)
*pIndex = i; if (pAddX)
*pAddX = nPosX - rData.aRef.aStart.Col(); if (pAddY)
*pAddY = nPosY - rData.aRef.aStart.Row();
if ( nCol2 > nCol1 + 1 && nRow2 > nRow1 + 1 && !bHiddenEdge )
{ // Only along the edges (The corners are hit twice) if ( nEdges & SCE_TOP )
rDocSh.PostPaint( nCol1, nRow1, nTab1, nCol2, nRow1, nTab2, PaintPartFlags::Marks ); if ( nEdges & SCE_LEFT )
rDocSh.PostPaint( nCol1, nRow1, nTab1, nCol1, nRow2, nTab2, PaintPartFlags::Marks ); if ( nEdges & SCE_RIGHT )
rDocSh.PostPaint( nCol2, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks ); if ( nEdges & SCE_BOTTOM )
rDocSh.PostPaint( nCol1, nRow2, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
} else// everything in one call
rDocSh.PostPaint( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
}
staticvoid lcl_PaintRefChanged( ScDocShell& rDocSh, const ScRange& rOldUn, const ScRange& rNewUn )
{ // Repaint for the parts of the frame in old, which in are no more in New
if ( nNewRow2 < nOldRow1 || nNewRow1 > nOldRow2 ||
nNewCol2 < nOldCol1 || nNewCol1 > nOldCol2 ||
( nNewCol1 != nOldCol1 && nNewRow1 != nOldRow1 &&
nNewCol2 != nOldCol2 && nNewRow2 != nOldRow2 ) )
{ // Completely removed or changed all sides // (check <= instead of < goes wrong for single rows/columns)
lcl_PaintOneRange( rDocSh, aOld, SCE_ALL );
} else// Test all four corners separately
{ // upper part if ( nNewRow1 < nOldRow1 ) // only delete upper line
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol1, nOldRow1, nTab1, nOldCol2, nOldRow1, nTab2 ), SCE_ALL ); elseif ( nNewRow1 > nOldRow1 ) // the upper part which is will be removed
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol1, nOldRow1, nTab1, nOldCol2, nNewRow1-1, nTab2 ),
SCE_ALL &~ SCE_BOTTOM );
// bottom part if ( nNewRow2 > nOldRow2 ) // only delete bottom line
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol1, nOldRow2, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL ); elseif ( nNewRow2 < nOldRow2 ) // the bottom part which is will be removed
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol1, nNewRow2+1, nTab1, nOldCol2, nOldRow2, nTab2 ),
SCE_ALL &~ SCE_TOP );
// left part if ( nNewCol1 < nOldCol1 ) // only delete left line
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol1, nOldRow1, nTab1, nOldCol1, nOldRow2, nTab2 ), SCE_ALL ); elseif ( nNewCol1 > nOldCol1 ) // the left part which is will be removed
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol1, nOldRow1, nTab1, nNewCol1-1, nOldRow2, nTab2 ),
SCE_ALL &~ SCE_RIGHT );
// right part if ( nNewCol2 > nOldCol2 ) // only delete right line
lcl_PaintOneRange( rDocSh, ScRange(
nOldCol2, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL ); elseif ( nNewCol2 < nOldCol2 ) // the right part which is will be removed
lcl_PaintOneRange( rDocSh, ScRange(
nNewCol2+1, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ),
SCE_ALL &~ SCE_LEFT );
}
}
SvxAdjust eSvxAdjust = SvxAdjust::Left; switch (eHorJust)
{ case SvxCellHorJustify::Left: case SvxCellHorJustify::Repeat: // not implemented case SvxCellHorJustify::Standard: // always Text if an EditCell type
eSvxAdjust = SvxAdjust::Left; break; case SvxCellHorJustify::Right:
eSvxAdjust = SvxAdjust::Right; break; case SvxCellHorJustify::Center:
eSvxAdjust = SvxAdjust::Center; break; case SvxCellHorJustify::Block:
eSvxAdjust = SvxAdjust::Block; break;
}
if (pName)
*pName = pURLField->GetRepresentation(); if (pUrl)
*pUrl = pURLField->GetURL(); if (pTarget)
*pTarget = pURLField->GetTargetFrame();
returntrue;
}
}
staticvoid lcl_SetEngineTextKeepingDefaults(const std::shared_ptr<ScFieldEditEngine>& pEngine,
ScDocument& rDoc, ScRefCellValue& rCell, const OUString& rURL)
{
std::unique_ptr<EditTextObject> pTextObj; if (rCell.getType() == CELLTYPE_EDIT)
{ if (rCell.getEditText())
pEngine->SetTextCurrentDefaults(*rCell.getEditText());
} else// Not an Edit cell and is a formula cell with 'Hyperlink' // function if we have no URL, otherwise it could be a formula // cell ( or other type ? ) with a hyperlink associated with it.
{ if (rURL.isEmpty())
pTextObj = rCell.getFormula()->CreateURLObject(); else
{
OUString aRepres = rURL;
// TODO: text content of formatted numbers can be different if (rCell.hasNumeric())
aRepres = OUString::number(rCell.getValue()); elseif (rCell.getType() == CELLTYPE_FORMULA)
aRepres = rCell.getFormula()->GetString().getString();
aLogicEdit.SetLeft( nStartX ); if (!bBreak)
aLogicEdit.SetRight( nStartX + nTextWidth );
// There is one glitch when dealing with a hyperlink cell and // the cell content is NUMERIC. This defaults to right aligned and // we need to adjust accordingly. if (aCell.hasNumeric() && eHorJust == SvxCellHorJustify::Standard)
{
aLogicEdit.SetRight( aLogicEdit.Left() + nThisColLogic - 1 );
aLogicEdit.SetLeft( aLogicEdit.Right() - nTextWidth );
}
aLogicEdit.SetBottom( aLogicEdit.Top() + nTextHeight );
Point aLogicClick = PixelToLogic(rPos,aEditMode); if ( aLogicEdit.Contains(aLogicClick) )
{
EditView aTempView(*pEngine, this);
aTempView.SetOutputArea( aLogicEdit );
OString ScGridWindow::getCellCursor() const
{ // GridWindow stores a shown cell cursor in mpOOCursors, hence // we can use that to determine whether we would want to be showing // one (client-side) for tiled rendering too. if (!mpOOCursors) return"EMPTY"_ostr;
if (comphelper::LibreOfficeKit::isCompatFlagSet(
comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs)) return mrViewData.describeCellCursorInPrintTwips();
// Send our cursor details to a view described by @pForShell, or all views // if @pForShell is null. In each case send the current view a cell-cursor // event, and others a cell_view_cursor event. // // NB. we need to re-construct the cursor details for each other view in their // own zoomed co-ordinate system (but not in scPrintTwipsMsgs mode). void ScGridWindow::updateKitCellCursor(const SfxViewShell* pForShell) const
{ if (comphelper::LibreOfficeKit::isCompatFlagSet(
comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
{
ScTabViewShell* pViewShell = mrViewData.GetViewShell(); // Generate the cursor info string just once and directly send to all. // Calling notifyKitCellViewCursor() would regenerate the // cursor-string unnecessarily.
OString aCursor = getCellCursor();
for (constauto& rRectangle : rRectangles)
{ // We explicitly create a copy, since we need to expand // the rectangle before coordinate conversion
tools::Rectangle aRectangle(rRectangle);
aRectangle.AdjustRight(1 );
aRectangle.AdjustBottom(1 );
/** * Turn the selection ranges rRectangles into the LibreOfficeKit selection, and send to other views. * * @param pLogicRects - if set then don't invoke the callback, just collect the rectangles in the pointed vector.
*/ void ScGridWindow::UpdateKitSelection(const std::vector<tools::Rectangle>& rRectangles, std::vector<tools::Rectangle>* pLogicRects)
{ if (!comphelper::LibreOfficeKit::isActive()) return;
// If this is true, rRectangles should already in print twips. // If false, rRectangles are in pixels. bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
if (bInPrintTwips)
{
SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", aRectListString); return;
}
for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
it = SfxViewShell::GetNext(*it))
{ if (it == pViewShell) continue; auto pOther = dynamic_cast<const ScTabViewShell *>(it); if (!pOther) return;
/** * Fetch the selection ranges for other views into the LibreOfficeKit selection, * map them into our view co-ordinates and send to our view.
*/ void ScGridWindow::updateOtherKitSelections() const
{
ScTabViewShell* pViewShell = mrViewData.GetViewShell(); bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
it = SfxViewShell::GetNext(*it))
{ auto pOther = dynamic_cast<const ScTabViewShell *>(it); if (!pOther) continue;
if (!comphelper::LibreOfficeKit::isActive() && !maVisibleRange.isInside(nX, nY))
{ if (maVisibleRange.mnCol2 < nX || maVisibleRange.mnRow2 < nY) return; // no further check needed, nothing visible
if (pPattern)
{ // fdo#87382 Also display the cell cursor for the visible part of // merged cells if the view position is part of merged cells. const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE); if (rMerge.GetColMerge() <= 1 && rMerge.GetRowMerge() <= 1) return; // not merged and invisible
SCCOL nX2 = nX + rMerge.GetColMerge() - 1;
SCROW nY2 = nY + rMerge.GetRowMerge() - 1; // Check if the middle or tail of the merged range is visible. if (maVisibleRange.mnCol1 > nX2 || maVisibleRange.mnRow1 > nY2) return; // no visible part
}
}
// don't show the cursor in overlapped cells constbool bOverlapped = pPattern && pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped();
// left or above of the screen? bool bVis = comphelper::LibreOfficeKit::isActive() || ( nX>=mrViewData.GetPosX(eHWhich) && nY>=mrViewData.GetPosY(eVWhich) ); if (!bVis)
{
SCCOL nEndX = nX;
SCROW nEndY = nY; if (pPattern)
{ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE); if (rMerge.GetColMerge() > 1)
nEndX += rMerge.GetColMerge()-1; if (rMerge.GetRowMerge() > 1)
nEndY += rMerge.GetRowMerge()-1;
}
bVis = ( nEndX>=mrViewData.GetPosX(eHWhich) && nEndY>=mrViewData.GetPosY(eVWhich) );
}
if ( !aPixelRects.empty() )
{ if (comphelper::LibreOfficeKit::isActive())
{
mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
updateKitCellCursor(nullptr);
} else
{ // #i70788# get the OverlayManager safely
rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
if (xOverlayManager.is())
{
ScModule* mod = ScModule::get();
Color aCursorColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor; if (mrViewData.GetActivePart() != eWhich) // non-active pane uses a different color.
aCursorColor = mod->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
std::vector< basegfx::B2DRange > aRanges; const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
// tdf#143733, tdf#145080 - improve border visibility // constants picked for maximum consistency at 100% and adequate response on zoom // line width = 1.5 at 100% (0.75 left +/- 0.75 right), 50% = 1, 200% = 1.25, 400% = 2.25 constdouble fCurZoom(mrViewData.GetZoomX()); constdouble fMinSize = 0.25 * GetDPIScaleFactor(); constdouble fAdjust(fMinSize + mrViewData.GetZoomX() * 0.5); int nAdjustPixel(o3tl::convert(fAdjust, o3tl::Length::pt, o3tl::Length::px)); // If zoom level is 50% or greater the rectangles must be at least 1 pixel thick if (fCurZoom >= 0.5)
nAdjustPixel = std::max(1, nAdjustPixel);
// Below each rectangle is adjusted so that they have thickness of nAdjustPixel // Left rectangle
basegfx::B2DRange aRBLeft(aPixelRects[0].Left() - nAdjustPixel, aPixelRects[0].Top() - nAdjustPixel,
aPixelRects[0].Right(), aPixelRects[0].Bottom() + nAdjustPixel);
aRBLeft.transform(aTransform);
aRanges.push_back(aRBLeft); // Right rectangle
basegfx::B2DRange aRBRight(aPixelRects[1].Left(), aPixelRects[1].Top() - nAdjustPixel,
aPixelRects[1].Right() + nAdjustPixel, aPixelRects[1].Bottom() + nAdjustPixel);
aRBRight.transform(aTransform);
aRanges.push_back(aRBRight); // Top rectangle
basegfx::B2DRange aRBTop(aPixelRects[2].Left() - nAdjustPixel, aPixelRects[2].Top() - nAdjustPixel,
aPixelRects[2].Right() + nAdjustPixel, aPixelRects[2].Bottom());
aRBTop.transform(aTransform);
aRanges.push_back(aRBTop); // Bottom rectangle
basegfx::B2DRange aRBBottom(aPixelRects[3].Left() - nAdjustPixel, aPixelRects[3].Top(),
aPixelRects[3].Right() + nAdjustPixel, aPixelRects[3].Bottom() + nAdjustPixel);
aRBBottom.transform(aTransform);
aRanges.push_back(aRBBottom);
ScModule* mod = ScModule::get(); const Color aBackgroundColor = mod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
Color aHighlightColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
aHighlightColor.Merge(aBackgroundColor, 100);
// tdf#162006 Ensures the AutoFill handle is visible at any zoom level // At 100% = Total Size 8 (2px for the external white line; 6px for the handle itself) constdouble fScaleFactor(2 + 2 * GetDPIScaleFactor()); constdouble fZoom(2 + 2 * mrViewData.GetZoomX());
Size aFillHandleSize(fScaleFactor + fZoom, fScaleFactor + fZoom);
ScModule* mod = ScModule::get();
Color aHandleColor = mod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor; if (mrViewData.GetActivePart() != eWhich) // non-active pane uses a different color.
aHandleColor = mod->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
// Pixel rectangle is in aInvertRect if ( !aInvertRect.IsEmpty() )
{ // #i70788# get the OverlayManager safely
rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
if ( !aPixRect.IsEmpty() )
{ // #i70788# get the OverlayManager safely
rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
// #i70788# central method to get the OverlayManager safely
rtl::Reference<sdr::overlay::OverlayManager> ScGridWindow::getOverlayManager() const
{
SdrPageView* pPV = mrViewData.GetView()->GetScDrawView()->GetSdrPageView();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.